react设置连续useState是什么赋值逻辑?

image.png

异步连续两次执行useState,两次设置的值都相同。先打印 cccccccccccccccc,后打印pppppppppppp

image.png

异步只执行一次,结果却是相反。

我的理解useEffect是监听a的变化后才执行,而且useState是异步的。理论来说不管是执行几次useState肯定都是先打印pppppppppppp再打印cccccccccccccccc吧?

但结果为什么会这样呢?完全理解不了,请大佬们帮忙解释下

难道是因为在异步请求中多次执行useState在赋值成功前会阻塞后续代码执行?是一种同步的场景?

react react-dom版本 16.13.1

阅读 1.7k
1 个回答

首先明确一个点,在 react18 之前,异步任务中 setState 不会被 batch(合并执行),也就是说如下两种场景存在更新流程的差异

异步任务中 setState

// 异步更新
function handlerAsync() {
    Promise.reslove().then(() => {
        setA(1);
        setA(2);
    })
}

在异步更新的流程中,由于没有 batch,所以组件会更新两次,下面浅析一下这两次更新操作在 react 中是如何调度的

  • setA(1), 向任务队列中插入一个更新任务 task1
  • setA(2), 向任务队列中插入一个更新任务 task2,并执行 task1
  • 调度器进行调度,异步执行 task2

可以看到,只有在多次 setA 操作下,前面的 setA 才会被同步执行。其实说白了,setA 是否同步更新,取决于后面是否还有 setA

所以如果这里只有一次 setA 操作,那么也是走的异步更新

  • setA(1), 向任务队列中插入一个更新任务 task1
  • 调度器进行调度,异步执行 task1

这也就是为什么在你的 demo 中,两次 setA,看到的是先 ccccccc,后 ppppppp, 原因就是第二个 setA 触发了第一个 setA 更新任务的同步执行,而第二个 setA 的更新任务则是正常被调度器异步调度执行的。如果你是这样写

const [a, setA] = useState(1);

function handlerAsync() {
    Promise.reslove().then(() => {
        setA(1);
        setA(2);
        console.log('pppppp');
    })
}

useEffect(() => {
    console.log('cccccc');
}, [a])

那么结果应该是

  • cccccc
  • pppppp
  • cccccc

同步任务中的 setState

// 同步更新
function handlerSync() {
    setA(1);
    setA(2);
}

同步的过程会进行 batch,因此只会有一次更新

  • setA(1), 向任务队列中插入一个更新任务 task1
  • setA(2), 向任务队列中插入一个更新任务 task2
  • 调度器进行调度,异步执行 (task1 -> task2)

最后

具体可以去看看 react-reconciler 部分 scheduleUpdateOnFiber 的实现。react18版本后,异步任务中的更新默认也是 batch 的,所以默认情况下不会出现你 demo 中的问题了
image.png

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏