<p dir="auto">I need to render React components on the server for SEO. My component fetches data in <code class="notranslate">ComponentWillMount</code>, based on the query parameters - but on the server (Node 4.0.0), <code class="notranslate">SetState</code> fails in the request's callback. The error can be reproduced with a simpler <code class="notranslate">setTimeout</code> too, as in the code example below.</p>
<p dir="auto">I have found numerous discussion on the web relating to complications between React and server-side rendering. I'm working on two work-around approaches:</p>
<ul dir="auto">
<li>removing all ajax requests from the server, instead rendering the result of the request directly into a global variable embedded in the first-serve HTML</li>
<li>moving the ajax request prior to initialization of the React components, on the server only (the request would still have to live in <code class="notranslate">ComponentWillMount</code> (or <code class="notranslate">ComponentDidMount</code>) for the client version.</li>
</ul>
<p dir="auto">Please let me know if there is an alternative or recommended approach instead.</p>
<p dir="auto">n.b. the reason I chose React over say Angular is because of <code class="notranslate">renderToString</code> is to ensure search engines can index the page, so this is all a bit of a bother for me right now...</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="var React = require('react');

// Reproduced in React 0.13.3 and 0.14.0-beta1
var ReactDOMServer = require(&quot;react-dom/server&quot;);

var A = React.createClass({
    componentWillMount: function() {
        var _this = this;
        // for example an ajax call to fetch data based on request parameters:
        setTimeout(function(err, res) {
            // state is set based on results
            _this.setState({ a: 1 });
        }, 100);
    },
    render: function() {
        return React.createElement('div', null);
    }
});


ReactDOMServer.renderToString(React.createElement(A, null));"><pre class="notranslate"><code class="notranslate">var React = require('react');

// Reproduced in React 0.13.3 and 0.14.0-beta1
var ReactDOMServer = require("react-dom/server");

var A = React.createClass({
    componentWillMount: function() {
        var _this = this;
        // for example an ajax call to fetch data based on request parameters:
        setTimeout(function(err, res) {
            // state is set based on results
            _this.setState({ a: 1 });
        }, 100);
    },
    render: function() {
        return React.createElement('div', null);
    }
});


ReactDOMServer.renderToString(React.createElement(A, null));
</code></pre></div>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$ node index.js 
/app/node_modules/react/lib/getActiveElement.js:25
    return document.body;
           ^

ReferenceError: document is not defined
    at getActiveElement (/app/node_modules/react/lib/getActiveElement.js:25:12)
    at ReactReconcileTransaction.ReactInputSelection.getSelectionInformation (/app/node_modules/react/lib/ReactInputSelection.js:38:23)
    at ReactReconcileTransaction.Mixin.initializeAll (/app/node_modules/react/lib/Transaction.js:168:75)
    at ReactReconcileTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:135:12)
    at ReactUpdatesFlushTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:136:20)
    at ReactUpdatesFlushTransaction.assign.perform (/app/node_modules/react/lib/ReactUpdates.js:86:38)
    at Object.flushBatchedUpdates (/app/node_modules/react/lib/ReactUpdates.js:147:19)
    at Object.wrapper [as flushBatchedUpdates] (/app/node_modules/react/lib/ReactPerf.js:66:21)
    at ReactDefaultBatchingStrategyTransaction.Mixin.closeAll (/app/node_modules/react/lib/Transaction.js:202:25)
    at ReactDefaultBatchingStrategyTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:149:16)"><pre class="notranslate"><code class="notranslate">$ node index.js 
/app/node_modules/react/lib/getActiveElement.js:25
    return document.body;
           ^

ReferenceError: document is not defined
    at getActiveElement (/app/node_modules/react/lib/getActiveElement.js:25:12)
    at ReactReconcileTransaction.ReactInputSelection.getSelectionInformation (/app/node_modules/react/lib/ReactInputSelection.js:38:23)
    at ReactReconcileTransaction.Mixin.initializeAll (/app/node_modules/react/lib/Transaction.js:168:75)
    at ReactReconcileTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:135:12)
    at ReactUpdatesFlushTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:136:20)
    at ReactUpdatesFlushTransaction.assign.perform (/app/node_modules/react/lib/ReactUpdates.js:86:38)
    at Object.flushBatchedUpdates (/app/node_modules/react/lib/ReactUpdates.js:147:19)
    at Object.wrapper [as flushBatchedUpdates] (/app/node_modules/react/lib/ReactPerf.js:66:21)
    at ReactDefaultBatchingStrategyTransaction.Mixin.closeAll (/app/node_modules/react/lib/Transaction.js:202:25)
    at ReactDefaultBatchingStrategyTransaction.Mixin.perform (/app/node_modules/react/lib/Transaction.js:149:16)
</code></pre></div>
<p dir="auto">This is a first-class API for sideways data loading of stateless (although potentially memoized) data from a global store/network/resource, potentially using props/state as input.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="type RecordOfObservables = { [key:string]: Observable&lt;mixed&gt; };

class Foo {

  observe(): RecordOfObservables {
    return {
      myContent: xhr(this.props.url)
    };
  }

  render() {
    var myContent : ?string = this.data.myContent;
    return &lt;div&gt;{myContent}&lt;/div&gt;;
  }

}"><pre class="notranslate"><span class="pl-s1">type</span> <span class="pl-v">RecordOfObservables</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span> <span class="pl-kos">[</span><span class="pl-s1">key</span>:<span class="pl-s1">string</span><span class="pl-kos">]</span>: <span class="pl-v">Observable</span><span class="pl-c1">&lt;</span><span class="pl-s1">mixed</span><span class="pl-c1">&gt;</span> <span class="pl-kos">}</span><span class="pl-kos">;</span>

<span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span>

  <span class="pl-en">observe</span><span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-v">RecordOfObservables</span> <span class="pl-kos">{</span>
    <span class="pl-k">return</span> <span class="pl-kos">{</span>
      <span class="pl-c1">myContent</span>: <span class="pl-en">xhr</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">props</span><span class="pl-kos">.</span><span class="pl-c1">url</span><span class="pl-kos">)</span>
    <span class="pl-kos">}</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span>

  <span class="pl-en">render</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">var</span> <span class="pl-s1">myContent</span> : ?<span class="pl-s1">string</span> <span class="pl-c1">=</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">data</span><span class="pl-kos">.</span><span class="pl-c1">myContent</span><span class="pl-kos">;</span>
    <span class="pl-k">return</span> <span class="pl-c1">&lt;</span><span class="pl-ent">div</span><span class="pl-c1">&gt;</span><span class="pl-kos">{</span><span class="pl-s1">myContent</span><span class="pl-kos">}</span><span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-ent">div</span><span class="pl-c1">&gt;</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span>

<span class="pl-kos">}</span></pre></div>
<p dir="auto">observe() executes after componentWillMount/componentWillUpdate but before render.</p>
<p dir="auto">For each key/value in the record. Subscribe to the Observable in the value.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="subscription = observable.subscribe({ onNext: handleNext });"><pre class="notranslate"><span class="pl-s1">subscription</span> <span class="pl-c1">=</span> <span class="pl-s1">observable</span><span class="pl-kos">.</span><span class="pl-en">subscribe</span><span class="pl-kos">(</span><span class="pl-kos">{</span> <span class="pl-c1">onNext</span>: <span class="pl-s1">handleNext</span> <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">We allow onNext to be synchronously invoked from subscribe. If it is, we set:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="this.data[key] = nextValue;"><pre class="notranslate"><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">data</span><span class="pl-kos">[</span><span class="pl-s1">key</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-s1">nextValue</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Otherwise we leave it as undefined for the initial render. (Maybe we set it to null?)</p>
<p dir="auto">Then render proceeds as usual.</p>
<p dir="auto">Every time onNext gets invoked, we schedule a new "this.data[key]" which effectively triggers a forcedUpdate on this component. If this is the only change, then observe is not reexecuted (componentWillUpdate -&gt; render -&gt; componentDidUpdate).</p>
<p dir="auto">If props / state changed (i.e. an update from recieveProps or setState), then observe() is reexecuted (during reconciliation).</p>
<p dir="auto">At this point we loop over the new record, and subscribe to all the new Observables.</p>
<p dir="auto">After that, unsubscribe to the previous Observables.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="subscription.dispose();"><pre class="notranslate"><span class="pl-s1">subscription</span><span class="pl-kos">.</span><span class="pl-en">dispose</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">This ordering is important since it allows the provider of data to do reference counting of their cache. I.e. I can cache data for as long as nobody listens to it. If I unsubscribed immediately, then the reference count would go down to zero before I subscribe to the same data again.</p>
<p dir="auto">When a component is unmounted, we automatically unsubscribe from all the active subscriptions.</p>
<p dir="auto">If the new subscription didn't immediately call onNext, then we will keep using the previous value.</p>
<p dir="auto">So if my <code class="notranslate">this.props.url</code> from my example changes, and I'm subscribing to a new URL, myContent will keep showing the content of the previous url until the next url has fully loaded.</p>
<p dir="auto">This has the same semantics as the <code class="notranslate">&lt;img /&gt;</code> tag. We've seen that, while this can be confusing and lead to inconsistencies it is a fairly sane default, and it is easier to make it show a spinner than it would be to have the opposite default.</p>
<p dir="auto">Best practice might be to immediately send a "null" value if you don't have the data cached. Another alternative is for an Observable to provide both the URL (or ID) and the content in the result.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="class Foo {

  observe() {
    return {
      user: loadUser(this.props.userID)
    };
  }

  render() {
    if (this.data.user.id !== this.props.userID) {
      // Ensure that we never show inconsistent userID / user.name combinations.
      return &lt;Spinner /&gt;;
    }
    return &lt;div&gt;Hello, {this.data.user.name} [{this.props.userID}]!&lt;/div&gt;;
  }

}"><pre class="notranslate"><span class="pl-k">class</span> <span class="pl-v">Foo</span> <span class="pl-kos">{</span>

  <span class="pl-en">observe</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">return</span> <span class="pl-kos">{</span>
      <span class="pl-c1">user</span>: <span class="pl-en">loadUser</span><span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">props</span><span class="pl-kos">.</span><span class="pl-c1">userID</span><span class="pl-kos">)</span>
    <span class="pl-kos">}</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span>

  <span class="pl-en">render</span><span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">data</span><span class="pl-kos">.</span><span class="pl-c1">user</span><span class="pl-kos">.</span><span class="pl-c1">id</span> <span class="pl-c1">!==</span> <span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">props</span><span class="pl-kos">.</span><span class="pl-c1">userID</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
      <span class="pl-c">// Ensure that we never show inconsistent userID / user.name combinations.</span>
      <span class="pl-k">return</span> <span class="pl-c1">&lt;</span><span class="pl-ent">Spinner</span> <span class="pl-c1">/</span><span class="pl-c1">&gt;</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>
    <span class="pl-k">return</span> <span class="pl-c1">&lt;</span><span class="pl-ent">div</span><span class="pl-c1">&gt;</span>Hello, <span class="pl-kos">{</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">data</span><span class="pl-kos">.</span><span class="pl-c1">user</span><span class="pl-kos">.</span><span class="pl-c1">name</span><span class="pl-kos">}</span> [<span class="pl-kos">{</span><span class="pl-smi">this</span><span class="pl-kos">.</span><span class="pl-c1">props</span><span class="pl-kos">.</span><span class="pl-c1">userID</span><span class="pl-kos">}</span>]!<span class="pl-c1">&lt;</span><span class="pl-c1">/</span><span class="pl-ent">div</span><span class="pl-c1">&gt;</span><span class="pl-kos">;</span>
  <span class="pl-kos">}</span>

<span class="pl-kos">}</span></pre></div>
<p dir="auto">We should use the RxJS contract of Observable since that is more in common use and allows synchronous execution, but once <a class="user-mention notranslate" data-hovercard-type="user" data-hovercard-url="/users/jhusain/hovercard" data-octo-click="hovercard-link-click" data-octo-dimensions="link_type:self" href="https://github.com/jhusain">@jhusain</a>'s proposal is in more common use, we'll switch to that contract instead.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="var subscription = observable.subscribe({ onNext, onError, onCompleted });
subscription.dispose();"><pre class="notranslate"><span class="pl-k">var</span> <span class="pl-s1">subscription</span> <span class="pl-c1">=</span> <span class="pl-s1">observable</span><span class="pl-kos">.</span><span class="pl-en">subscribe</span><span class="pl-kos">(</span><span class="pl-kos">{</span> onNext<span class="pl-kos">,</span> onError<span class="pl-kos">,</span> onCompleted <span class="pl-kos">}</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-s1">subscription</span><span class="pl-kos">.</span><span class="pl-en">dispose</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">We can add more life-cycle hooks that respond to these events if necessary.</p>
<p dir="auto">Note: This concept allows sideways data to behave like "behaviors" - just like props. This means that we don't have to overload the notion state for these things. It allows for optimizations such as throwing away the data only to resubscribe later. It is restorable.</p>