跳到主要内容

在 react 18 版本以前


在同步环境中异步,在异步环境中同步。

setState 本身并不具备绝对的同步/异步概念。 比如:在 promise 的 then()方法中、setTimeOut()、setInterVal(),ajax 的回调等异步环境中,setState 就是同步的。同步环境下就是异步的。

react 会有一个上下文环境,在同步环境中,setState 处于 react 的上下文中,react 会监控动作合并,所以这个时候 setState()是异步的。

而在异步环境中,比如 promise 的 then()方法中、setTimeOut()、setInterVal()中,react 实际上已经脱离了 react 的上下文环境。所以 setState()是同步执行的。

代码如下(示例):setState()的同步和异步的案例

const [state, setState] = useState(0); //初始化为 0
//当前处于同步环境,所以setState是异步的
setState(1);
console.log(state); //此时因为setState是异步的,state还未被赋值 打印结果是 0

setTimeOut(() => {
//当前处于异步环境,所以setState()是同步的
setState(5);
console.log(state); //打印结果是5
}, 0);

setState()在 react 中一开始之所以被设定为异步触发,是因为每当触发 setState 时 react 需要去更新视图。

通过异步更新视图来合并在短时间内的大量 setState 动作。 举个例子:如果一短时间内 setState()调用了 100 次,视图是否需要相对应的渲染 100?所以为了防止这种情况,setState 设置成异步,合并短时间内的多次改动,并集中统一渲染。

这样,当我们在短时间内多次的调用 setState 时,视图只会被更新一次。

代码如下(示例):setState()同步环境中异步执行 --合并动作案例

const [state,setState] = useState(0);

//快速给state赋值,连续赋值五次
for(let i =0;i<5;++i){
setState(i);
}
//实际上,因为setState动作合并的特性,视图只会被更新一次而不是五次

以上案例中,虽然 setState 被赋值了五次,但,视图不会更新五次。因为动作合并的原因,视图只会更新一次,也就等于只执行了 setState(4); 但是!! 这是 setState 在同步环境中触发异步更新机制的时候。 如果是在异步环境中呢? 我们看看案例。

代码如下(示例):setState()异步环境中同步执行 --不合并请求案例

const [state,setState] = useState(0);

//异步环境中快速给state赋值,连续赋值五次
Promise.resolve().then(() => {
//当前处于异步环境。setStat同步进行
for(let i =0;i<5;++i){
setState(i);
}
})

//在这次案例中,上面setState货真价实的被赋值了5次。视图也真真切切的被更新了五次

上面案例中,setState 所处与异步环境中,所以同步执行,也就是不再进行等待直接修改 state 的值不进行请求合并,而 state 的值一旦被修改。react 的视图就会更新。所以上面案例中,视图会被更新五次。

在 react 18 版本以后


setState()不论在同步环境还是异步环境都是异步的。

总结

  • react 18 版本之前,setState 在同步环境中异步执行,异步环境中同步执行。
  • react 18 版本之后,不论在同步环境还是在异步环境中,setState 都是异步的。
  • setState 之所以会设置成异步是为了合并短时间内的多次渲染。
信息

作者:工边页字
链接:https://juejin.cn/post/7107233888119816229
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。