阅读 219

setState 更新状态行为理解「useState 同理」

更新状态 setState

react 类式组件更新页面可以通过组件实例访问 this.setState(stateChange, callback) 方法。

  1. setState 函数,可以接收两个参数,作用:更新状态,重新调用当前组件的 render 渲染

    • 第一个参数:为对象或者函数,在框架底层将此对象本身或者函数调用后的返回值与当前状态,进行类似 Object.assign()合并操作

      // 对象 this.setState({   counter: this.state.counter + this.props.increment, }) // 函数 this.setState((preState, props) => ({   counter: preState.counter + props.increment })) 复制代码

    • 第二个参数:为回调函数,在状态数据合并操作完毕后,调用组件的 render 渲染函数之前,才执行第二个参数回调。

      render() {     let { count } = this.state     return <div>         <button onClick={() => {             this.setState({ count: count + 1 }, () => {                 console.log('改变之前 count', count);                 console.log('改变之后 count', this.state.count);             })         }}>点我加1</button>         当前计数器为{count}     </div> } 复制代码

  2. setState() 这一句调用代码本身是同步执行,但是【合并操作】与 【调用 render 渲染函数】这两个底层行为都是异步执行的(微任务)。所以要小心,在同步执行的代码中取值行为,如下代码,两次取值之后,合并操作与重新渲染组件才会执行,所以两次取值都是相同的。

    state = { count: 0 } // ❌ 第二次调用,this.state.count 值与第一次调用取值是一样的,都是 0 + 1 handle = () => {     this.setState({ count: this.state.count + 1 }, () => {            ...      })     this.setState({ count: this.state.count + 1 }, () => {         ...      }) } // ✅ 将 setState 的第一个参数写为函数即可收到每次更新的值 handle = () => {     this.setState(preState => ({ count: preState.count + 1 }), () => {         ...     })     this.setState(preState => ({ count: preState.count + 1 }), () => {         ...     }) } 复制代码

setState 执行流程

没看过源码或分析源码文章求证,纯自测,符合测试结果

  1. 当 setState 函数第一个参数为对象时:

    1. 执行合并操作任务,将第一个参数对象与当前 state 对象合并(状态数据更新了)

    2. 执行 setState 的第二个参数回调

    3. 执行当前组件的 render 函数(视图更新了)

    4. 执行同步代码: setState 函数调用,第一个参数对象成员取值,注册下面的 3 个异步微任务,继续执行后面的同步代码

    5. 清空(执行)当前宏任务中的微任务:

  2. 当 setState 函数第一个参数为函数时:

    1. 执行第一个参数函数,拿到返回值对象,将对象成员取值

    2. 执行合并操作任务,将返回值对象与当前 state 对象合并(状态数据更新了)

    3. 执行 setState 的第二个参数回调

    4. 执行当前组件的 render 函数(视图更新了)

    5. 执行同步代码: setState 函数调用,第一个参数函数定义,注册下面的 4 个异步微任务,继续执行后面的同步代码

    6. 清空(执行)当前宏任务中的微任务:

useState Hook 中的 setXXX 与 class 组件的 setState 函数执行流程一样,唯二区别是:1. setXXX 没有重载,只有一个参数,没有第二个参数回调。2. setXXX 更新状态是替换操作,并非合并操作

多次调用合并渲染

当多次调用 setState 函数时,react 做了性能优化。

  1. 同步代码环境中调用 setState:在正常的 react 的事件流里,一个类式组件多次执行 setState 或者函数组件某个 useState 中的 setXXX 多次执行,只会调用一次重新渲染 render,称为合并渲染

    如果没有合并渲染,在每次执行 setState 更新函数时,组件都要重新 render 一次,会造成无效渲染,浪费时间(因为最后一次渲染会覆盖掉前面所有的渲染效果)

    所以 react 会把一些可以一起更新的 setState 放在一起,进行合并,只渲染最后一次。

  2. 异步代码环境中调用 setState:在 setTimeout,Promise.then 等异步事件中,多次执行 setState 和 useState 中的 setXXX,每次执行都会调用 render 渲染函数

    setTimeout 已经超出了 react 的控制范围,react 无法对 setTimeout 的回调代码前后加上事务逻辑(除非 react 重写 setTimeout)。

    当遇到 setTimeout/setInterval/Promise.then(fn)/fetch 回调/xhr 网络回调 时,react 都是无法控制的。


作者:梓梁
链接:https://juejin.cn/post/7032916821959245861


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐