React
控制的事件处理程序,以及生命周期内调用setState
是异步更新state
React
控制以外的事件中调用setState
是同步更新state
,好比原生js绑定事件、setTimeout
/setInrerval
等。setState
的“异步”并非说内部由异步代码实现,自己的执行过程和代码都是同步的。算法
之因此会有一种异步方法的表现形式,归根结底仍是由于React
框架自己的性能机制所致使的。由于每次调用setState
都会触发更新,异步操做是为了提升性能,将多个状态合并一块儿更新,减小re-render
调用。性能优化
咱们来看一些例子:框架
state = { number: 1 } componentDidMount() { this.setState({ number: 3 }); console.log(this.state.number); // 1 }
因而可知该事件处理程序中的setState
是异步更新state
的。异步
state = { number: 1 } componentDidMount() { setTimeout(() => { this.setState({ number: 3 }); }, 0); console.log(this.state.number); // 3 }
上面咱们讲到,setState
自己并非一个异步方法,之因此会变现出一种异步的形式,是由于React
框架自己的一种性能优化机制。那么基于这一点,假如咱们能绕过React
的机制,就能够令setState
以同步的形式体现。函数
state = { number: 1 } componentDidMount() { document.body.addEventListener('click', this.resetState, false); } resetState() { this.setState({ number: 3 }); console.log(this.state.number); // 3 }
一样的,原生事件也能够绕过React
的性能优化机制,达到同步更新的表现。性能
在React
的setState
函数实现中,会根据一个变量isBatchingUpdates
判断是否直接更新this.state
,仍是放入队列中延时更新。优化
而isBatchingUpdates
默认是false
,标识setState
是同步更新this.state
。可是有一个函数batchedUpdates
会把isBatchingUpdates
修改成true
,而当React
在调用事件处理函数以前就会先调用这个函数将isBatchingUpdates
修改成true
。这样由React
控制的事件处理过程setState
就不会同步更新this.state
。this
function enqueueUpdate(component){ //injected注入的 ensureInjected(); //若是不处于批量更新模式 if(!batchingStrategy.isBatchingUpdates){ batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } //若是处于批量更新模式 dirtyComponents.push(component); }
事实上setState
内部的执行过程是很复杂的,大体过程包括更新state
,建立新的VNode
,再通过diff
算法对比差别,决定渲染哪一部分以及怎么渲染,最终造成最新的UI。这一过程包含组件的四个生命周期函数:spa
shouComponentUpdate
componentWillUpdate
render
componentDidUpdate
若是是子组件而且依赖父组件,还会执行一个钩子函数componentWillReceiveProps
。code
假如setState
是同步更新的,每次更新这个过程都要完整执行一次,无疑会形成性能问题。事实上这些生命周期为纯函数,对性能还好,可是diff比较、更新DOM总消耗时间和性能吧。
在“异步”中若是对同一个值进行屡次setState
, setState
的批量更新策略会对其进行覆盖,取最后一次的执行。
state = { number: 1 } handleClick() { const number = this.state.number; this.setState({ number: number + 1 }); this.setState({ number: number + 1 }); this.setState({ number: number + 1 }); }
最终的结果只加了1。
若是是同时 setState
多个不一样的值,在更新时会对其进行合并批量更新。
hanldeClick() { this.setState({ name: 'Clearlove' }); this.setState({ age: 18 }); }
在hanldeClick
处理程序中调用了两次setState
,可是render
只执行了一次。由于React
会将多个this.setState
产生的修改放在一个队列里进行批延时处理。
setState
提供了一个回调函数供开发者使用,在回调函数中,咱们能够实时的获取到更新以后的数据。仍是以刚才的例子作示范:
state = { number: 1 } componentDidMount() { this.setState({ number: 3 }, () => { console.log(this.state.number); // 3 }); }