How to synchronize state from DOM with my React component in an isomorphic application?

My problem

I am creating an isomorphic application in React that first renders the component on the server side, and then uses the React browser side intelligent re-rendering.

I came across a situation where the DOM may no longer synchronize with the state of the React component before React can first display the browser side. This can happen when the user is on a slow internet connection and the react.js file takes some time to download (which is also the reason that I am creating an isomorphic application)

Example

Here is an example that I put together to show this: http://jsfiddle.net/jesstelford/z4o44esb

  • Run this example
  • Togle checkbox
  • Click "Render React"
  • The current state of React is displayed in the console.
  • Note that it is still set to {done: false}, which is incorrect
 var TodoItem = React.createClass({ // ... render: function() { return ( <label> <input type="checkbox" defaultChecked={this.state.done} onChange={this.onChange} /> {this.props.name} </label> ); } }); // User toggles checkbox ON here, before React is rendered browser-side // render using React browser-side var renderedComponent = React.render(component, document.getElementById('content')); // Incorrectly outputs { done: false } console.log('React state:', renderedComponent.state); 

Possible (half) solution

I found one possible solution using React refs : http://jsfiddle.net/jesstelford/z4o44esb/2

 var TodoItem = React.createClass({ // ... syncStateFromDOM: function() { this.setDone(this.refs.done.getDOMNode().checked); }, render: function() { return ( <label> <input ref="done" type="checkbox" defaultChecked={this.state.done} onChange={this.onChange} /> {this.props.name} </label> ); } }); // User toggles checkbox ON here, before React is rendered browser-side // render using React browser-side var renderedComponent = React.render(component, document.getElementById('content')); // Sync state from the DOM renderedComponent.syncStateFromDOM() // Correctly outputs { done: true } console.log('React state:', renderedComponent.state); 

The disadvantages of this approach are:

  • status is synchronized after the DOM is displayed
  • Requires additional code external to the component itself for synchronization on first rendering

My question

When pre-rendering the React Component server, there is some way to synchronize the state of the DOM with this React component before it displays the browser side, since the DOM was processed by the user before React loaded on the browser side

Thanks!

+7
reactjs isomorphic-javascript
source share
1 answer

This is an intriguing question! At a high level, the problem is that React does not catch the "change" event, which bubbles up to the top-level component when you click this checkbox because it has not yet been created on the client side. Your half solution handles this by manually simulating an onChange call. I started thinking that you might need an onChange queue of events ... but then I realized that React already has everything you need.

The three-quarter solution is to simply rename syncStateFromDOM to componentDidMount and not even name it manually. According to the docs, in the most recent versions of React, componentDidMount is called only in the browser, and it accesses the life cycle after the components are mounted (i.e. When React.render is about to return). This is the perfect place for your use case. See: http://jsfiddle.net/qdt4z3w9/

This solves your problem with code external to the component itself! But after the initial rendering is complete, state tuning is still taking place. Unfortunately, I think that this is basically the way React works - in order to be able to map existing DOM nodes to refs, each component must be fully installed first. But the extra virtual DOM diff is a small price to pay, as it is designed for quick lightning.

Hope this helps!

+5
source share

All Articles