React setState是同步还是异步的?
class IndexPage extends React.Component {
constructor(props) {
super(props);
this.state = {
val: 0
};
}
componentWillMount() { // 组件挂载前触发
}
componentDidMount() {
this.setState({ val: this.state.val + 1 })
console.log(this.state.val) // 0
this.setState({ val: this.state.val + 1 })
console.log(this.state.val) // 0
setTimeout(() => {
this.setState({ val: this.state.val + 1 })
console.log(this.state.val) // 2
this.setState({ val: this.state.val + 1 })
console.log(this.state.val) // 3
}, 0)
}
shouldComponentUpdate(nextProps, nextState) {
// 组件Props或者state改变时触发,true:更新,false:不更新
return true
}
componentWillUpdate(nextProps, nextState) { // 组件更新前触发
}
componentDidUpdate() { // 组件更新后触发
}
componentWillUnmount() { // 组件卸载时触发
}
render() {
const { val }=this.state;
console.log(val, '哈哈') // 0 1 2 3
return (
<div>
我是首页{val}
</div>);
}
}
- react内部为了优化setState()的批处理,会对setState()进行合并,并且对相同属性的设置
只保留最后一次
的设置,类似于函数Object.assign()。
- 在 React 的 setState 函数实现中,会根据一个变量
isBatchingUpdates
判断是直接更新 this.state 还是放到一个updateQueue
中延时更新,而isBatchingUpdates
默认是 false,表示 setState 会同步更新 this.state;但是,有一个函数batchedUpdates
,该函数会把isBatchingUpdates
修改为 true,而当 React 在调用事件处理函数之前就会先调用这个batchedUpdates
将isBatchingUpdates
修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state,而是异步的。
结合上面分析的,钩子函数中的 setState 无法立马拿到更新后的值,所以前两次都是输出0,当执行到 setTimeout 里的时候,前面两个state的值已经被更新,由于 setState 批量更新的策略, this.state.val 只对最后一次的生效,为1,而在 setTimmout 中 setState 是可以同步拿到更新结果,所以 setTimeout 中的两次输出2,3,最终结果就为 0, 0, 2, 3 。
setState({},()=>{})参数
componentDidMount() {
this.setState({ val: this.state.val + 1 }, () => {
console.log(this.state.val, 'www') // 1
})
console.log(this.state.val) // 0
}
- 传递对象:批处理,对相同变量进行的多次处理会合并为一个,并以最后一次的处理结果为准。
- 传递函数:链式调用,React 会把我们更新 state 的函数加入到一个队列里面,然后,按照函数的顺序依次调用,不会合并为一次。同时,为每个函数传入 state 的前一个状态,这样,就能更合理的来更新我们的 state 了,该函数有两个参数prevState和props。
总结:
异步的情况
由React控制的事件处理函数,以及生命周期函数调用setState时表现为异步 。大部分开发中用到的都是React封装的事件,比如onChange、onClick、onTouchMove等(合成事件中)以及钩子函数中,这些事件处理函数中的setState都是异步处理的。
同步的情况:
React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval,ajax,promise.then内等 React 无法掌控的 APIs情况下,setState是同步更新state的。
原理:
根据isBatchingUpdates
来判断是否批量更新,如果当前setState命中了batchUpdate
机制,就会异步执行,如果未能命中,则会同步执行