React hooks:为啥异步函数中的多个 useState 设置器会导致多次重新渲染?

Posted

技术标签:

【中文标题】React hooks:为啥异步函数中的多个 useState 设置器会导致多次重新渲染?【英文标题】:React hooks: Why do several useState setters in an async function cause several rerenders?React hooks:为什么异步函数中的多个 useState 设置器会导致多次重新渲染? 【发布时间】:2021-12-19 15:23:01 【问题描述】:

下面这个 onClick 回调函数会导致 1 次重新渲染:

const handleClickSync = () => 
  // Order of setters doesn't matter - React lumps all state changes together
  // The result is one single re-rendering
  setValue("two");
  setIsCondition(true);
  setNumber(2);
;

React 将所有三个状态更改集中在一起并导致 1 次重新渲染。

然而,下面的 onClick 回调函数会导致 3 次重新渲染:

const handleClickAsync = () => 
  setTimeout(() => 
    // Inside of an async function (here: setTimeout) the order of setter functions matters.
    setValue("two");
    setIsCondition(true);
    setNumber(2);
  );
;

每个useState setter 都会重新渲染一次。此外,setter 的顺序会影响每个渲染中的值。

问题:为什么我将函数设为异步(此处通过setTimeout)会导致状态更改一个接一个地发生,从而导致 3 次重新渲染。如果函数是同步的,为什么 React 会将这些状态变化混为一谈,只导致一次重新渲染?

您可以使用this CodeSandBox 来体验该行为。

【问题讨论】:

因为你不在 react 自己的调用范围内。首先,react 将调用 click 处理程序,并且 setstate 调用将在它结束时完成。在第二种情况下,它发生在任意时间,因此反应不会对其进行批处理。阅读blog.isquaredsoftware.com/2020/05/… - 或者更确切地说,阅读整篇文章。我认为这是任何使用 react 的人都应该阅读的内容的一部分。 虽然没有解释原因,但React documentation suggests “根据哪些值往往会一起变化,将状态[拆分] 为多个状态变量。” 这意味着无论何时可能,如果大多数值一起更改,则使用对象作为状态就可以了。 【参考方案1】:

如果代码执行在 react 内部开始(例如,onClick 侦听器或useEffect),那么 react 可以确保在您完成所有状态设置后,执行将返回到 react 并且它可以从那里继续。所以对于这些情况,它可以让代码继续执行,等待返回,然后同步进行单次渲染。

但是如果代码执行是随机开始的(例如,在setTimeout 中,或者通过解析一个promise),那么当你完成时代码不会返回响应。所以从 react 的角度来看,它正在安静地睡觉,然后你打电话给setState,迫使 react 像“啊哈!他们正在设置状态!我最好渲染”。有一些异步方式可以让 react 等待查看您是否在做更多事情(例如,超时 0 或微任务),但没有同步方式可以让 react 知道您何时完成。

在当前版本的 react 中,您可以使用 unstable_batchedUpdates 告诉 react 批量更改多个更改:

import  unstable_batchedUpdates  from "react-dom";

const handleClickAsync = () => 
  setTimeout(() => 
    unstable_batchedUpdates(() => 
      setValue("two");
      setIsCondition(true);
      setNumber(2);    
    );
  );
;

一旦 react 18 到来,这将不是必需的,因为他们对并发模式的渲染所做的更改将不再需要这个。

【讨论】:

这也适用于通过手动事件侦听器更新状态时,例如:element.addEventListener(),原因与提到的在 react 外部触发的更新相同。【参考方案2】:

现在只对事件处理程序中的批次同步 setStates 做出反应。 但在 react 18 中,它将在 setTimeoutuseEffects 等中提供 这是 Dan https://github.com/reactwg/react-18/discussions/21 的出色解释

【讨论】:

以上是关于React hooks:为啥异步函数中的多个 useState 设置器会导致多次重新渲染?的主要内容,如果未能解决你的问题,请参考以下文章

React Hooks异步操作防踩坑指南

如何返回异步箭头功能?

React Hooks-处理异步api调用

为啥 React setState hook 没有立即更新? [复制]

React hooks的useState原理

React(^16.8) 新增特性Hook