将状态变量设置为其当前值时“重新渲染次数过多”

问题描述 投票:0回答:2

React 文档指出:

如果将状态变量设置为当前值,即使没有

memo
,React 也会跳过重新渲染组件。您可能仍然会看到您的组件函数被额外调用一次,但结果将被丢弃。

[链接]

我认为这意味着,在如下所示的组件中:

const Component = memo(function () {
    const [foo, setFoo] = useState('x');

    setFoo(foo);     // no-op?

    return null;
});

setFoo(foo)
语句实际上不会做任何事情,因为它显然将
foo
设置为它已有的值,所以React不会触发新的渲染。

但是,当我尝试这个(在 Create React App 项目中)时,我发现它会导致此错误:

错误:重新渲染次数过多。 React 限制渲染次数以防止无限循环。

在我的测试中,我发现无论有没有

<React.StrictMode>
,在开发模式(
npm start
)和生产模式(
npm run build
)下,无论有还是没有
memo
,都会发生同样的情况
setFoo(foo)
并与
setFoo(val => val)
。这发生在一个新的 Create React App 项目中,所以它对我的设置来说不能太具体。

由于文档提到“您可能仍然会看到您的组件函数被额外调用一次”(在这种情况下,额外时间将重新调用

setFoo
),我认为也许这只是错误消息是有点错误,应该说“对组件功能的重复调用太多”而不是“重新渲染太多”;但不,我确认,如果我跟踪调用次数并最终停止重新调用
setFoo
,React 会渲染从 last 调用组件函数返回的内容,而不是从 first 调用返回的内容,所以即使医生所说的“结果将被丢弃”似乎也不是真的。

我错过了什么?文档有错吗?

reactjs create-react-app
2个回答
0
投票

凭直觉,我测试了如果我写

setTimeout(() => setFoo(foo), 10)
而不是
setFoo(foo)
会发生什么——这样状态是在 10 毫秒延迟后设置的,而不是在组件函数实际运行时设置的——在这种情况下,它做到了 not 触发新的渲染。 (事实上,它甚至没有额外调用组件函数。)

所以文档中的说法似乎是:

如果将状态变量设置为当前值,即使没有

memo
,React 也会跳过重新渲染组件。您可能仍然会看到您的组件函数被额外调用一次,但结果将被丢弃。

如果状态变量在调用组件函数期间被设置,则不适用;在这种情况下,即使状态变量被设置为其当前值,React 也一定会丢弃正在进行的渲染并触发新的调用。

(对于是否应该改变这种行为,我将保留评论。)


0
投票

文档中在

useState
的特殊用例下提到了这一点:存储以前渲染的信息

允许在组件渲染期间调用

setState
函数来立即以新状态重新渲染组件。这可用于根据当前状态和先前状态之间的差异显示信息,而不需要较低的性能
useEffect

请参阅文档中的此警告:

请注意,如果在渲染时调用 set 函数,则它必须位于类似

prevCount !== count
的条件内,并且条件内部必须存在类似
setPrevCount(count)
的调用。否则,您的组件将循环重新渲染,直到崩溃。另外,您只能像这样更新当前渲染组件的状态。在渲染过程中调用另一个组件的
set
函数是错误的。

© www.soinside.com 2019 - 2024. All rights reserved.