React钩子:为什么一个异步函数中的几个useState setter会导致几个reender



下面的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);
});
};

对于每个useStatesetter,这是一个重新渲染。此外,设置器的顺序会影响每个渲染中的值。

问题:为什么我使函数异步(此处通过setTimeout(会导致状态一个接一个地发生变化,从而导致3次重新渲染。如果函数是同步的,只导致一个重新应答,为什么React会把这些状态变化放在一起?

你可以玩这个CodeSandBox来体验这种行为。

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

但是,如果代码执行是随机开始的(例如,在setTimeout中,或者通过解析promise(,那么当您完成时,代码就不会返回来做出反应。所以从react的角度来看,它在安静地睡觉,然后你调用setState,迫使react像";啊!他们正在设置状态!我最好渲染一下";。有一些异步的反应方式可以等着看你是否在做更多的事情(例如,超时0或微任务(,但没有一种同步的反应方式来知道你什么时候完成了。

您可以使用unstable_batchedUpdates:来判断对批处理多个更改的反应

import { unstable_batchedUpdates } from "react-dom";
const handleClickAsync = () => {
setTimeout(() => {
unstable_batchedUpdates(() => {
setValue("two");
setIsCondition(true);
setNumber(2);    
});
});
};

在版本18中,这是不必要的,因为他们对并发渲染的渲染所做的更改使批处理在所有情况下都有效。

现在只对事件处理程序内的setState进行批处理同步。但在react 18中,它将在setTimeoutuseEffect等中可用这是丹的精彩解释https://github.com/reactwg/react-18/discussions/21

更新: REACT 18功能自动批量

在React中,当调用setState()时,批处理有助于减少状态更改时发生的重新渲染次数。以前,React在事件处理程序中批量更新状态,例如:

const handleClick = () => {
setCounter();
setActive();
setValue();
}
//re-rendered once at the end.

但是,在事件处理程序之外发生的状态更新没有被批处理。例如,如果您有承诺或正在进行网络呼叫,则不会对状态更新进行批处理。像这样:

fetch('/network').then( () => {
setCounter(); //re-rendered 1 times
setActive();  //re-rendered 2 times
setValue();   //re-rendered 3 times
});
//Total 3 re-renders

正如你所知,这不是表演。React 18引入了自动批处理,允许对所有状态更新进行批处理,即使是在promise、setTimeouts和事件回调中也是如此。这大大减少了React必须在后台进行的工作。React将等待微任务完成后再重新渲染。

React中提供开箱即用的自动配料,但如果您想退出,可以使用flushSync

更多阅读免费营地资源:https://www.freecodecamp.org/news/react-18-new-features/

最新更新