近来在学习react源码, 最初是直接从入口一行一行的看, 结果跟着调用的函数跳转来跳去头都晕了. 后来决定带着一个目的去看源码, 每次看只研究一个东西. 一开始最想了解的就是充满魔性的setState. 本文是我对setState的一些理解, 不当之处欢迎留言指正.
setState的魔性看一下下边几个例子的输出情况.
例1 合成事件中的setState
import React from 'react'; export default class SetState extends React.Component { constructor(props) { super(props); } state = { count: 0 } click = () => { this.setState({ count: this.state.count + 1, }) console.log('count1', this.state.count); this.setState({ count: this.state.count + 1, }); console.log('count2', this.state.count); } render() { return ( <div onClick={this.click}> count的值{this.state.count} </div> ) } } // 打印: // count1 0 // count2 0例2 生命周期函数中的setState
import React from 'react'; export default class SetState extends React.Component { constructor(props) { super(props); } state = { count: 0 } componentDidMount () { this.setState({ count: this.state.count + 1, }) console.log('count1', this.state.count); this.setState({ count: this.state.count + 1, }); console.log('count2', this.state.count); } render() { return ( <div> count的值{this.state.count} </div> ) } } // 打印: // count1 0 // count2 0例3 setTimeout中的setState
import React from 'react'; export default class SetState extends React.Component { constructor(props) { super(props); } state = { count: 0 } componentDidMount () { setTimeout(() => { this.setState({ count: this.state.count + 1, }) console.log('count1', this.state.count); this.setState({ count: this.state.count + 1, }); console.log('count2', this.state.count); }, 0); } render() { return ( <div> count的值{this.state.count} </div> ) } } // 打印: // count1 1 // count2 2例4 Promise中的setState
import React from 'react'; export default class SetState extends React.Component { constructor(props) { super(props); } state = { count: 0 } componentDidMount () { Promise.resolve() .then(() => { this.setState({ count: this.state.count + 1, }) console.log('count1', this.state.count); this.setState({ count: this.state.count + 1, }); console.log('count2', this.state.count); }) } render() { return ( <div> count的值{this.state.count} </div> ) } } // 打印: // count1 1 // count2 2从例1和例2的输出结果来看, 在setState后直接取state的值发现并没有更新, setState对state的更新似乎是个异步的过程;
而从例3, 例4输出结果来看, setState又是一个同步更新state的操作, 可以立即拿到更新的结果.
也就是说, setState有的时候是异步的有的时候是同步的, 真是非常的魔性. 根据网上的一些文章和自己的实验可以得出如下结论.
在合成事件, 生命周期函数中的setState是异步批量更新的, 不能立即拿到更新的结果, 多次setState只会走一次render
在setTimeOut, setInterval, 原生事件, Promise中的setState是同步逐个更新的, 可以立即拿到更新的state, 而且每次setState都会走一次render
关于是批量更新还是非批量更新可以在render函数中打印查看
setState魔性表现揭秘 理解setState的异步批量更新下边是个异步批量更新的示意图
这里将在合成事件, setTimeout等中的写的代码的调用称为Main Process.
例如下边componentDidMount中的代码的执行都叫Main process.
componentDidMount () { this.setState({ count: this.state.count + 1, }); console.log('count1', this.state.count); this.setState({ count: this.state.count + 1, }); console.log('count2', this.state.count); }直接结合这段代码分析上边的这个看起来很牛x的图.
首先执行一个setState, 判断是setState操作, 创建一个更新任务加入更新队列, 交给协调中心, 协调中心判断不需要更新, 继续执行main Process中的代码.
遇到第一个console, 直接执行, 打印时取出了state, 显然state没更新还是原来的值, 然后再执行Main Process代码.
遇到第二个setState, 注意此时取出的state是没有更新的, 再创建一个更新任务到更新队列, 交给协调中心, 协调中心判断不需要更新, 继续执行main Process中的代码. 然后执行了console, 取出的state是没更新的.
一定时间后, 协调中心再次调度, 发现可以更新了, 然后执行了更新队列的两个任务, 得到一个新的state, 然后更新this.state和视图.
从以上分析可以了解到为什么两个console打印的都是之前的值.