我在React中构建一个Isomorphic Application,它首先渲染一个组件服务器端,然后利用React的智能重新渲染浏览器端.
我遇到过这种情况,在React能够首先呈现浏览器端之前,DOM可能与React组件的状态不同步.当用户处于慢速Internet连接时,会发生这种情况,并且react.js文件需要一段时间才能下载(这也是我构建同构应用程序的原因)
例
这是我放在一起展示这种情况的一个例子:http://jsfiddle.net/jesstelford/z4o44esb
>运行此示例
>勾选复选框
>点击“渲染反应”
>当前的React状态在控制台中输出
>请注意,它仍设置为{done:false},这是不正确的
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);
可能的(一半)解决方案
我找到了一个使用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,document.getElementById('content')); // Sync state from the DOM renderedComponent.syncStateFromDOM() // Correctly outputs { done: true } console.log('React state:',renderedComponent.state);
这种方法的缺点是:
>在呈现DOM之后状态会同步
>需要组件本身外部的额外代码才能在首次渲染时进行同步
我的问题
在预渲染React组件服务器端时,是否有任何方法可以在呈现浏览器端之前将DOM状态同步到该React组件,因为在React加载浏览器端之前用户已经操纵了DOM?
谢谢!
解决方法
“四分之三”解决方案是简单地将syncStateFromDOM重命名为componentDidMount
,甚至不用手动调用它.根据文档,在最新版本的React中,componentDidMount仅在浏览器中调用,它是组件安装后的生命周期回调(即React.render即将返回时).它是您用例的理想之选.见:http://jsfiddle.net/qdt4z3w9/
这解决了组件本身外部代码的问题!但是在原始渲染发生之后仍然存在状态设置.不幸的是,我认为这基本上就是React的工作方式 – 为了能够将现有的DOM节点与refs相匹配,每个组件都需要先完全挂载.但是额外的虚拟DOM差异是一个很小的代价,因为它的设计是闪电般快速的.
希望这可以帮助!