深入剖析setState同步异步机制 (2)

能命中batchUpdate的场景包括:生命周期和其调用函数、React中注册的事件和其调用函数。总之,是React可以“管理”的入口,关键是“入口”。

这里要注意一点:React去加isBatchingUpdate的行为不是针对“函数”,而是针对“入口”。比如setTimeout、setInterval、自定义DOM事件的回调等,这些都是React“管不到”的入口,所以不会去其首尾设置isBatchingUpdates变量。

concurrent 模式一定是异步更新

因为这个东西只在实验阶段,所以要开启 concurrent 模式,同样需要将 react 升级为实验版本,安装如下依赖:

npm install react@experimental react-dom@experimental

其他代码不用变,只更改 index 文件如下:

- ReactDOM.render(<App />, document.getElementById('root'));

+ ReactDOM.unstable_createRoot(document.getElementById('root')).render(<App />);

则可以发现:其更新都是同步的,在任何情况下都是如此。

关于函数式组件中 useState 的 setter

在函数式组件中,我们会这样定义状态:

const [count, setCount] = useState(0)

这时候,我们发现当我们无论在同步函数还是在异步回调中调用 setCount 时,打印出来的 count 都是旧值,这时候我们会说:setCount 是异步的。

  const [count, setCount] = useState(0);

  // 直接调用
  const handleStrightUpdate = () => {
    setCount(1);
    console.log(count); // 0
  };

  // 放在setTimeout回调中
  const handleSetTimeoutUpdate = () => {
    setTimeout(() => {
      setCount(1);
      console.log(count); // 0
    });
  };

setCount 是异步的,这确实没错,但是产生上述现象的原因不只是异步更新这么简单。原因主要有以下两点:

1,调用 setCount 时,会做合并处理,异步更新该函数式组件对应的 hooks 链表里面的值,然后触发重渲染(re-renders),从这个角度上来说,setCount确实是一个异步操作;

2,函数式的capture-value特性决定了console.log(count)语句打印的始终是一个只存在于当前帧的常量,所以就算无论 setCount 是不是同步的,这里都会打印出旧值。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzzyp.html