setState真的是异步的吗



当我学习React时,每个人都告诉我setState是异步的,因为如果我console.log(state)正好在setState之后,它将打印旧值。然而,当我检查源代码时,useState没有返回任何promise,也没有使用async/await。

意味着setState必须同步。但是,如果它是同步的,那么为什么React在我调用setState时不立即重新渲染组件?正如您在下面的代码片段中看到的,首先打印0,然后打印render1

如果这是因为批处理,那么批处理是如何工作的?怎么可能";等待";直到触发App组件重新渲染的特定时间。更重要的是,React如何知道在这种情况下需要调用App而不是其他函数?

export default function App(){
const [count, setCount] = useState(0);
console.log("render", count);
return <button onClick={() => {
setCount(count+1); // why doesn't App get called/re-rendered right away here
console.log(count); // this prints the old value first then re-render happens later
}}>
</button>
}

首先,我要提醒您,console.log(count)注销旧值的原因与sync与async无关。注销旧值的原因是count是本地const,它永远不会更改。它以旧价值观开始存在,无论时间流逝多少,无论操作顺序如何,它都将永远是旧价值观。

调用setCount不会更改计数中的值,它只是要求react重新提交组件。当新的渲染发生时,将创建一组具有新值的新局部变量。但是上一次渲染的console.log无法与下一次渲染中的值交互。


也就是说,react中的渲染确实(通常(会短暂延迟,以批量处理多个更改。我不确定他们做这件事的确切方式,因为有几种可能性。我以前看过他们的代码,但在过去几年里发生了足够多的变化,我怀疑我的发现是否仍然有用。在任何情况下,他们可以批量重新发送的方式包括:

  1. setTimeout。当你第一次设置状态时,他们可能会设置一个超时,很快就会熄灭。如果发生另一个设置状态,他们会注意到该状态,但不必设置额外的超时。稍晚一点,超时将停止,渲染将发生。

  2. 微任务。最简单的方法是Promise.resolve().then(/* insert code here */)。一旦当前调用堆栈完成执行,微任务就会运行。这种方法的优点是它可以比setTimeout更快地发生,因为超时具有最小的持续时间

  3. 如果执行开始于react内部,他们可以等到代码返回后再进行渲染。这曾经是反应批处理更改的主要方式。例如,他们有一段代码负责调用useEffects。在此期间,它会记录状态变化,但还没有重新发送。最终您返回,由于返回将代码执行重新交给react,它会检查哪些状态已更改,并在需要时重新发送。

React如何知道在这种情况下需要调用App而不是其他函数?

它知道这一点,因为您更改的状态是App的状态。React知道组件的树结构,所以它知道它只需要呈现您设置状态的组件及其子级。

最新更新