参考:
解决问题:如何让React的setState变为同步
btnClick1
两次同时给state.a加1.在React控制的时候,始终多次运行,只会加一次,而且因为setState不同步,console的输出始终先于setState
btnClick1=()=>{ this.setState({ a: this.state.a + 1 }); this.setState({ a: this.state.a + 1 }); console.log('点击时的a的值为:',this.state.a); }
btnClick3
两次同时给state.a加1.在React控制的时候,始终多次运行setState,会执行多次,而且因为setState跳出了Reactde异步控制,console的输出就会取到setState之后的值。
btnClick3 = ()=>{ setTimeout(() => { this.setState({ a: this.state.a + 1 }); this.setState({ a: this.state.a + 1 }); console.log('点击时的a的值',this.state.a); },0) }
结论(React的setState批量更新原理)
异步:React 会先找到我们注册的 vnode 和 vnode 内的对应事件,从而在执行前,先把 isBatchingUpdate这个变量打开。只要我们的方法没完成,由于变量锁的存在,就会一直让我们的修改只停留在更新中状态内,一直不会更新到实际的 state
上。直到我们的方法执行完,事务的后置函数就会关闭我们的 isBatchingUpdate,并执行渲染操作,至此整个批量更新就完成了。
同步:setTimeout 里面会同步是由于 setTimeout会把里面的函数放到下一个宏任务内,这样就刚好跳出了事务的控制,就会显示出同步更新的情况。这里就是Javascript 的 Event-loop 机制;另外,在原生事件中,绕过了React,不会触发isBatchingUpdates变量的改变,所以也会同步进行更新渲染
完整代码:
菜鸟教程提供的运行环境:替换为下列代码,可以再页面和F12控制台(mac:option_command_i)查看运行结果
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>菜鸟教程 React 实例</title> <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script> <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script> <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script> </head> <body> <div id="example"></div> <script type="text/babel"> class App extends React.Component{ constructor(props){ super(props); } state = { a: 1 } componentDidMount(){ console.log('a的初始值为:',this.state.a); } btnClick1=()=>{ this.setState({ a: this.state.a + 1 }); this.setState({ a: this.state.a + 1 }); console.log('点击时的a的值为:',this.state.a); } sState = ()=>{ this.setState({ a: this.state.a + 1 }); this.setState({ a: this.state.a + 1 }); } btnClick2=()=>{ this.sState(); console.log('点击时的a的值为:',this.state.a); } btnClick3 = ()=>{ setTimeout(() => { this.setState({ a: this.state.a + 1 }); this.setState({ a: this.state.a + 1 }); console.log('点击时的a的值',this.state.a); },0) } render(){ return ( <div> <div>hello william</div> <button onClick={this.btnClick2}>点击按钮</button> <div>{this.state.a}</div> </div> ) } } ReactDOM.render( <App />, document.getElementById('example') ); </script> </body> </html>