在 React 中创建函数组件并设置默认参数时,一切都按预期工作,组件将渲染一次。但是,一旦您添加像useEffect
这样的钩子并在依赖项数组中使用此参数,组件就会永久重新渲染。 我在这里创建了一个简单的演示:https://codesandbox.io/s/infinite-useeffect-loop-on-default-value-tv7hj?file=/src/TestComponent.jsx
原因很明显,因为当使用对象作为默认参数时,它将再次创建并且不会等于前一个对象。当然,这不会发生在原始默认参数值(如数字或字符串)上。
除了使用defaultProps
之外,还有更好的方法来避免这种副作用吗?
是的,不要将默认值value
设置为对象,只需将其设置为 false。然后检查value
是否真实,如果是,则访问正确的属性,否则,仅显示默认值。新代码。
它将是这样的:
import { useEffect, useState } from "react";
const TestComponent = ({ value = false }) => {
const [calcValue, setCalcValue] = useState(0);
useEffect(() => {
setCalcValue((cur) => cur + 1);
}, [value]);
return (
<div>
{value ? value.name : "Test"}:{calcValue}
</div>
);
};
你得到无限循环的原因是value
的引用不断变化。
第一次渲染组件时,它会看到对值的新引用,这会触发useEffect
,进而修改组件的状态,这会导致新的渲染,这会导致value
再次重新创建,因为对该变量的旧引用已更改。
处理此问题的最简单方法是在组件外部创建一个默认值并使用它(与defaultProps
解决方案基本相同):
import { useEffect, useState } from "react";
const defaultValue = {name: "Test"}; // <-- default here
const TestComponent = ({ value = defaultValue }) => {
const [calcValue, setCalcValue] = useState(0);
useEffect(() => {
setCalcValue((cur) => cur + 1);
}, [value]);
return (
<div>
{value.name}:{calcValue}
</div>
);
};
这样做将确保组件每次渲染时,它都会看到相同的value
引用,因此useEffect
钩子只运行一次。
另一种处理方法是首先用memo
包装你的组件,然后创建一个新的状态变量,它采用原始value
,并使你的useEffect
钩依赖于这个新的状态变量:
const TestComponent = React.memo(({ value = {name: "Test"} }) => {
const [calcValue, setCalcValue] = useState(0);
const [myValue, setMyValue] = useState(value);
useEffect(() => {
setCalcValue((cur) => cur + 1);
}, [myValue]);
return (
<div>
{myValue.name}:{calcValue}
</div>
);
});
我们用memo
包装组件的原因是,如果 prop 的值(而不是引用)发生了变化,它只会在状态更改后重新渲染。您可以通过提供自定义比较功能作为第二个参数来更改检测道具更改memo
方式。