-
Notifications
You must be signed in to change notification settings - Fork 46.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Server-rendering should be distinct from client-rendering #1979
Comments
It's unclear to me what you're proposing. We already have ReactServerRenderingTransaction internally which makes a few things behave differently when doing server rendering. What else were you thinking might differ? |
The way server-rendering is handled internally does not allow for different nodes to be generated, only different attributes. What I'm proposing is exposing an API for being able to have server-rendered markup be (potentially) entirely different to the client-rendered markup. For the purpose of better SEO, UX and speed. What I'm suggesting is the general idea of this:
I.e that you can generate completely different markup server-side without affecting client-side performance, the script-block above could theoretically allow the button to show/hide the Content-component until the page has finished loading, the same approach could also be used for SEO-purposes or for limiting the size of the generated server-side markup. Personally, my So to me it seems that exposing e.g. |
I'm assuming contexts would be the right and super easy solution to this, it would just need to be standardized to really make work and be usable throughout. |
It would be cool if there was a built-in way to parse this server-side code out of the JSX client-targeted bundle at bundling time. I know there's a variety of tools for the job, but something officially-recommended would aid the isomorphic app story. |
@yaycmyk "parse this server-side code out of the JSX client-targeted bundle at bundling time" huh? |
Don't we already re-use existing markup for isomorphic apps? Isn't that the point of the the checksum? I'm not entirely sure I understand the motivation for having components render different markup if they're on the server side. You're just adding more branches/codepaths that need to be tested/maintained (the point of isomorphic rendering is to avoid exactly that, otherwise, just implement your SEO version separately). Also, sites are penalized if the content they display to users deviates from what they show Google, so I'm not entirely convinced this is a good SEO story in the first place. My intuition is that: We don't recommend this, but we aren't going to stop you. If you're going to do this, you can use a global. This is probably not behavior that third party components should be participating in. Is there a specific example usecase that isn't easily handled with a implementation-specifc global? |
@JSFB You can't do feature detection server-side, hence most complex inline styles and polyfills are impossible to do server-side, there's also no way to generate placeholder content and apply the styles/polyfills once the client-side mounts as there is no transition taking place. I worked on a Picture and ImgSet polyfill which works great, but it's impossible to make them work at all when server-rendering, it simply cannot be done at current. |
Perhaps I'm miss-understanding, but couldn't you do a forceUpdate when the page loads to update the placeholder content? Perhaps triggered by onMount, which I think doesn't get called server-side. I'm still feeling like a global+useragent is sufficient for the vast majority of cases. It would be useful to have a specific example of a piece of markup that is both useful/desirable and unworkable server-side. I'm not saying such examples don't exist, but I looking for a persuasive example. |
You're still lacking a way to find out if you are server- or client-rendering. Hmm, yes I guess the neat thing about the
Anything that requires feature detection; i.e. vendor styles and polyfills by and large. User agent sniffing is generally a big no-no and very fragile for feature detection... but it can work and could be the ultimate approach in a way, but you would still need a backup tl;dr |
@syranide You wouldn't want to ship server-targeted markup to the client, would you? I was just suggesting it would nice to have an officially-supported, holistic approach to the development of isomorphic apps using React. |
@JSFB Oh right, lazy images/lists/etc require something like this too to be server-renderable. |
@syranide what if this can be re-stated as: "a more clearly defined transition between server rendering and client render should be made available"? @JSFB To me, this means, since components are state-full, we want a well defined hand-off where a component (and its state) can be evidenced as leaving the server (some server-side events for serialization) and then coming into the client (some client-side events for deserialization). I think environment and feature detection can then be identified / diagnosed from the context as / when necessary, but handling how server -> client transitions affect components needs to be somehow surfaced: |
@kryptt Yeah I think so. Really it all just boils down to being able to determine client/server somehow and there being an update when the transition happens, i.e. (I assume) a value in context would be sufficient. |
I'm in favour of this idea. I believe this would have the side effect of fixing one of my issues. ie: These warnings when I conditionally use different content on the client and server (eg: a static link to home on the server and a JS navigation on the client) based on a
I probably won't use it much, but I also like the idea of being able to control the serialization and deserialization of components on the client. |
You should be able to do this with context fairly easily in 0.14. |
One issue related to isomorphic apps, context, and setState in component{Will,Did}Mount comes to mind. Or rather, one mess.
So
|
My use case for this, is to prefix for all user agent on the server side and only for the browser agent on the client side. As pointed out here facebook/react#1979 I think that it's a very good trade-off. This allow me to remove the user agent from the memoization I use on the `renderToString` method. The test fails because of robinweser/inline-style-prefixer#58. I'm gonna address it. Fix #546.
It is not clear to me how this problem was solved (if it was). I still don't see how to bundle different packages for server/client and render something different without the checksum warning. What is the warning about btw? What does it actually mean for React? I could not find any real solution. Hopefully i'm wrong, because it would mean that it is impossible to use React for isomorphic apps in so many scenarios. Well, at least all my scenarios: involving canvas, svg, video, animations... a lot of code and related modules that aren't needed on the server - and in some cases break the code with "window is not defined". I tried to do something like this: But of course that didn't work. Any idea on how to use React to precisely define 2 different bundles/markups in an isomorphic way without incurring in the warning? |
The browser version is usually bundled separately. Normally you'd just outside of react use a technique like that used in the is-browser package. Basically you just have a module that returns |
Thanks @dantman If someone had an example of |
I just wanted to add that the most common way to have different client/server markup is to set a flag in componentDidMount() {
this.setState({ hasMounted: true });
} Then you can use |
I outlined a possible solution that works today above. I’ll close this issue but if you feel strongly this needs a separate API or a change in behavior please file an RFC and describe how you think it should work: https://github.com/reactjs/rfcs Thanks! |
We managed to solve it by using es2015 const asyncLoader = ({ loader }) => {
class AsyncLoader extends Component {
constructor() {
super();
this.state = { };
}
componentDidMount() {
loader().then((bundle) => {
const AsyncComponent = (bundle && bundle.default) ? bundle.default : bundle;
this.setState({ AsyncComponent });
});
}
render() {
const { AsyncComponent } = this.state;
return AsyncComponent && (<AsyncComponent {...this.props} />);
}
}
return AsyncLoader;
}; and then use it to make any component async by doing # path/to/component/index.jsx
const AnyComponent = () => <div />;
export default AnyComponent
# path/to/component/async.js
export default asyncLoader({
loader: () => import('./index'),
}); which then can be imported by: import AnyComponent from 'path/to/component/async' the key here is, not all components need to be rendered in its full glory on SSR, SEO mostly only care about simple stuff like meta tags and text.. and users are generally okay with seeing above the fold fast feedback, even though they're incomplete.. hence, components which requires polyfill or big dependencies should be delayed and be loaded async (which would also help in improving the initial bundle size - faster page load).. |
My use case for this, is to prefix for all user agent on the server side and only for the browser agent on the client side. As pointed out here facebook/react#1979 I think that it's a very good trade-off. This allow me to remove the user agent from the memoization I use on the `renderToString` method. The test fails because of robinweser/inline-style-prefixer#58. I'm gonna address it. Fix FormidableLabs/radium#546.
Kind of related to #1978 (concerns polyfills)
It seems to me that we should really treat server-rendering as distinct from client-rendering, they have different requirements and priorities.
So I propose that we treat server-rendering as uniquely distinct from client-rendering, e.g. as a separate flag
React.serverSide = true
(which would default to true if document is not available). It's even imaginable that we could extend this with e.g.React.targetClient = 'SEO' / 'legacyClient' / 'everGreenClient'
, which would optionally allow the markup to be tailored for a specific purpose.So when reusing server-rendered markup client-side:
I'm not really affected by this, but for those that do use server-rendering; this seems like the correct way of approaching it (as opposed to just rendering everything exactly the same). However, I'm working on
img
- andpicture
-element polyfills, but without this they cannot be made to generate sensible server-side markup.PS. I realize this can be done today using a global + mixin, but without a standardized solution, it's unlikely that third-party (and polyfill) components could ever participate in this. But that doesn't mean that it needs to be shipped with React even (none of this requires core support I believe), as long as it's standardized.
The text was updated successfully, but these errors were encountered: