[英]React: useEffect “paradox” with eventHandler props
这个例子有问题。
我有一个Input
组件。 这个输入组件有一个onChange
属性来处理内部的任何数据更改。
应该是,如果我从外部更新value
,则不会触发onChange
道具,因为输入组件内部没有任何更改。 如果你开始输入onChange
道具就会被触发,我也可以从外部更新状态。
但是如果满足某些条件(在我的示例中只是一个布尔状态),我想从内部预填充输入字段,因此我必须调用onChange
事件,以便应用程序可以从外部更新状态。
问题/悖论是useEffect
检查条件现在需要onChange
道具作为依赖项,并且由于此道具是一种方法,因此它会在每次渲染时更改。 这意味着现在输入元素总是在任何更改时重置为 useEffect 的值,因为 useEffect 再次触发。
如果允许我从 useEffect 依赖项中删除onChange
属性,我的问题就不存在了,但这当然违反了 useEffect 钩子的规则。
我想到的唯一“解决方案”:
我在不同的形状场景中遇到过这个问题,所以我真的希望得到一个可以应用于一般类似场景的答案。 也许我的代码中有一个关于如何使用事件处理程序的反模式?
预先感谢您的所有答案^^。
我会将 if 语句更改为仅在prefillCondition
已更改时运行。 这意味着您需要知道上次渲染的值是多少。 这样做内联看起来像:
const [prefillCondition, setPrefillCondition] = React.useState(false);
const previous = useRef();
React.useEffect(() => {
previous.current = prefillCondition;
})
React.useEffect(() => {
// On the first render, previous.current will be undefined. On every render after that
// it will tell you what value prefillCondition had on the previous render.
if (prefillCondition && !previous.current) {
onChange("value from useEeffff");
}
}, [onChange, prefillCondition, previous]);
我发现想要记住上一个渲染中的值是很常见的。 如果您也这样做,您可能需要考虑将该逻辑提取到自定义钩子中:
const usePrevious = (value) => {
const previous = useRef();
React.useEffect(() => {
previous.current = value;
})
return previous.current;
}
// used like:
const [prefillCondition, setPrefillCondition] = React.useState(false);
const prevPrefillCondition = usePrevious(prefillCondition);
React.useEffect(() => {
if (prefillCondition && !prevPrefillCondition) {
onChange("value from useEeffff");
}
}, [onChange, prefillCondition, prevPrefillCondition]);
考虑这个
const a = () => 'foo bar'
const b = () => 'foo bar'
a == b // false
即使该函数具有相同的代码,它也具有不同的内存引用,因此对于 React 而言,它始终显示为一个新的(已更改的)道具,这就是它调用重新渲染的原因,并且由于您总是传递新的匿名 onChange 回调,因此它每次都会调用 useEffect。
所以你可以从依赖项数组中省略它(注意 ESlint 规则)
React.useEffect(() => {
// this would supposibly fetch some default value based on conditions and the call an onChange
if (prefillCondition) {
onChange("value from useEeffff");
}
}, [prefillCondition]); // eslint-disable-line react-hooks/exhaustive-deps
或使用React.useCallback()
函数,它可以准确地解决您的问题并防止在每次渲染时创建新函数。
const App = () => {
const [myVal, setMyVal] = React.useState("");
const handler = React.useCallback((a) => {
console.log("onchange", a);
setMyVal(a);
}, []);
return (
<div>
<Input value={myVal} onChange={handler} />
<div>
<button
onClick={() => {
setMyVal("smth");
}}
>
set to smth from outside
</button>
</div>
</div>
);
};
你不能只在输入上设置一个默认值
defaultValue={prefillCondition ? "text here" : ""}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.