I'm relatively new to ReactJS and have been seduced by the ease of implementing server-side rendering to reduce "time to first tweet". I'm running a Node-Express-React stack which pre-renders the markup on the server using React's renderComponentToString.
It works fine when the components can be rendered synchronously, however I'm struggling when it comes to implementing ajax-populated components (however this applies to any asynchronous operation during the initialization of the component, e.g. websocket).
Take the example from the React website: http://facebook.github.io/react/tips/initial-ajax.html
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
this.setState({
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
It won't work on the server, since componentDidMount is never triggered when using renderComponentToString. This simple case can be worked around by using the same HTTP request wrapper on the client and on the server (instead of using jQuery's $.get), and by pre-fetching the data before instantiating the component and passing it as a prop.
However, in an actual, complex app, asynchronous dependencies can get very complicated and pre-fetching doesn't really fit the descendant approach of building React structures. How can I implement an asynchronous initialization pattern in React that can be rendered on the server without actually mounting anything (i.e. no DOM emulation a la PhantomJS, which is the whole point of using renderComponentToString) ?
Unlike with synchronous, a user visiting pages that utilize the asynchronous mode gets to see the page content without having to wait for the page to display the whole content. The type of rendering mode loads pages independently from the ads.
React sets its state asynchronously because doing otherwise can result in an expensive operation. Making it synchronous might leave the browser unresponsive. Asynchronous setState calls are batched to provide a better user experience and performance.
I believe the most practical way to do this is to make an optional prop for the preloaded data, like so:
getInitialState: function() {
if (this.props.initialLastGist) {
var lastGist = this.props.initialLastGist;
return {
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
};
} else {
return {};
}
},
componentDidMount: function() {
if (!this.props.initialLastGist) {
$.get(this.props.source, function(result) {
var lastGist = result[0];
this.setState({
username: lastGist.user.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
}
},
With a setup like this, the component can be rendered immediately if the preloaded data is present; otherwise the AJAX request will be sent while mounting.
Currently, server rendering is always synchronous and componentDidMount
isn't called on the server because it usually involves DOM manipulation. Sorry I don't have a better solution here right now, but in general you want to minimize the number of HTTP requests to the server so it's worth thinking through your architecture so that you can collect all the data you need on the server.
You could take a look at the react-quickstart project, which uses react-async
and ships with a server-rendering example. react-async
provides a decorated renderComponentToString
method that pre-fetches asynchronous state and renders it into the initial markup. However, you will need to specify asynchronous state separately from 'regular' state.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With