• setState 是同步还是异步 测试


    class 类组件中的 setState 和 hooks 函数组件中的 useState 的 状态修改函数 是一样的,有时同步,有时异步。

    结论:

    1. setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。
    2. setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
    3. setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和 setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新。
    • 原生事件: 指的是绕过 React 通过 addEventListener 直接添加的事件处理函数
    • 钩子函数: 各个生命周期函数
    • 合成事件:由 React 引发的事件处理(比如通过 onClick 引发的事件处理)

    React是怎样控制异步和同步的呢?

    在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中延时更新,而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;但是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会先调用这个 batchedUpdates 将 isBatchingUpdates 修改为true,这样由 React 控制的事件处理过程 setState 不会同步更新 this.state。

    总结:

    只要你进入了 react 的调度流程,那就是异步的。只要你没有进入 react 的调度流程,那就是同步的。什么东西不会进入 react 的调度流程? setTimeout setInterval ,直接在 DOM 上绑定原生事件等。这些都不会走 React 的调度流程,你在这种情况下调用 setState ,那这次 setState 就是同步的。 否则就是异步的。
    而 setState 同步执行的情况下, DOM 也会被同步更新,也就意味着如果你多次 setState ,会导致多次更新,这是毫无意义并且浪费性能的。

    类组件测试:

    import React, { PureComponent } from "react";
    
    class setStateTest extends PureComponent {
      constructor() {
        super();
        this.state = {
          num: 0,
          str: "111",
          bool: false
        }
      };
    
      componentDidMount() {
    // 在 componentDidMount 生命周期里,连续修改 state ,只会触发一次render,state 被放在任务队列中批量更新,此时它们是异步的。
        this.setState({
          num: 3
        })
        console.log(this.state.num); // 0
        this.setState({
          str: "222"
        })
        this.setState({
          num: 2
        })
        console.log(this.state.num); // 0
    
    // 在定时器里会逐条触发 render ,说明此时是同步
        setTimeout(() => { 
          this.setState({
            num: 3
          })
          console.log(this.state.num); // 3
          this.setState({
            str: "222"
          })
          this.setState({
            num: 2
          })
          console.log(this.state.num); // 2
        }, 5000)
      };
    
      render() {
        const { num, str, bool } = this.state;
        console.log("触发render", num, str, bool);
        return (
          <>
            <button onClick={() => { // 两个 setState 都执行了,但是只刷新了一次,所以它是异步的?
              this.setState({
                num: num + 1
              })
              this.setState({
                bool: !bool
              })
            }}>setState同步异步测试</button>
          </>
        )
      };
    };
    
    export default setStateTest;
    

    函数式组件测试:

    import React, { useEffect, useState } from "react";
    
    export default function setStateTest() {
      const [num, setNum] = useState(0);
      const [str, setStr] = useState('11111');
    
      useEffect(() => {
        // 异步处理,会合并计算,值触发一次 刷新
        setNum(2)
        console.log(num); // 0 因为是异步处理,所以这里打印的是初始值 0
        setNum(3)
        console.log(num); // 0
        setNum(4)
        setStr("22222")
    
        // 在定时器里会逐条触发 render ,说明此时是同步
        // 为何 hooks 组件中 定时器 里面没有拿到更新后的值,这里是因为闭包的原因,只能拿到 初始 值 0
        setTimeout(() => {
          setNum(2)
          console.log(num); // 0
          setNum(3)
          console.log(num); // 0
          setNum(1)
          setStr("33333")
        }, 3000)
      }, [])
    
      return (
        <>
          <button onClick={() => {
            setTimeout(() => { // 在定时器里会逐条触发 render ,说明此时是同步
              setNum(2)
              console.log(num); // 看点击的时候,当前的 num 是多少就是多少
              setNum(3)
              console.log(num); // 同上
              setNum(1)
              setStr("33333")
            }, 0)
          }}>{console.log("刷新了")}点击事件</button>
        </>
      )
    };
    
  • 相关阅读:
    php类型转换
    PHP标记
    使用PHP从web访问mysql数据库
    javascript string对象的属性与方法
    linux vim 常用命令
    添加事件监听兼容IE6-8
    js-jQuery对象与dom对象相互转换
    js 数组
    js正则表达式
    选择排序
  • 原文地址:https://www.cnblogs.com/MrZhujl/p/16206018.html
Copyright © 2020-2023  润新知