[英]React: useEffect “paradox” with eventHandler props
Problem with this example . 这个例子有问题。
I've got an Input
component.我有一个
Input
组件。 This input component has an onChange
prop to handle any data changes from inside .这个输入组件有一个
onChange
属性来处理内部的任何数据更改。
As it should be, if I update the value
from outside , the onChange
prop is not fired, since nothing changed from inside the input component.应该是,如果我从外部更新
value
,则不会触发onChange
道具,因为输入组件内部没有任何更改。 If you start typing the onChange
prop is fired and I can update the state from outside as well.如果你开始输入
onChange
道具就会被触发,我也可以从外部更新状态。
But if some condition is met (in my example just a boolean state), I want to prefill the input field from inside, therefore I'll have to call an onChange
event so that the App can update the state from outside.但是如果满足某些条件(在我的示例中只是一个布尔状态),我想从内部预填充输入字段,因此我必须调用
onChange
事件,以便应用程序可以从外部更新状态。
The problem/paradox is that the useEffect
checking for a condition now needs the onChange
prop as a dependency, and since this prop is a method, it will change on every render.问题/悖论是
useEffect
检查条件现在需要onChange
道具作为依赖项,并且由于此道具是一种方法,因此它会在每次渲染时更改。 This means now the input element always resets to the value from the useEffect when anything is changed because the useEffect is triggered again.这意味着现在输入元素总是在任何更改时重置为 useEffect 的值,因为 useEffect 再次触发。
My problem would not be there if I was allowed to remove the onChange
prop from the useEffect dependency, but this is of course against the rules of the useEffect hook.如果允许我从 useEffect 依赖项中删除
onChange
属性,我的问题就不存在了,但这当然违反了 useEffect 钩子的规则。
The only "solutions" that came to my mind:我想到的唯一“解决方案”:
I've come across this problem in different shapes scenarios, so I really hope to get an answer that can be applied to similar scenarios in general.我在不同的形状场景中遇到过这个问题,所以我真的希望得到一个可以应用于一般类似场景的答案。 Maybe I just have an anti-pattern in my code of how I work with event handlers?
也许我的代码中有一个关于如何使用事件处理程序的反模式?
Thank you in advance for all answers ^^.预先感谢您的所有答案^^。
I would change the if statement to only run if prefillCondition
has changed.我会将 if 语句更改为仅在
prefillCondition
已更改时运行。 That means you'll need to know what the value was on the last render.这意味着您需要知道上次渲染的值是多少。 Doing that inline would look like:
这样做内联看起来像:
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]);
I find it common to want to remember a value from the previous render.我发现想要记住上一个渲染中的值是很常见的。 If you do too, you may want to consider extracting that logic to a custom hook:
如果您也这样做,您可能需要考虑将该逻辑提取到自定义钩子中:
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]);
Consider this考虑这个
const a = () => 'foo bar'
const b = () => 'foo bar'
a == b // false
Even though the function has the same code it has a different memory reference so to React it always appears as a new (changed) prop, that's why it calls re-render and since you always pass new anonymous onChange callback it calls useEffect each time.即使该函数具有相同的代码,它也具有不同的内存引用,因此对于 React 而言,它始终显示为一个新的(已更改的)道具,这就是它调用重新渲染的原因,并且由于您总是传递新的匿名 onChange 回调,因此它每次都会调用 useEffect。
So you could either omit it from the dependencies array (note the ESlint rule)所以你可以从依赖项数组中省略它(注意 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
or useReact.useCallback()
function which solves exactly your problem and prevents creating a new function on every render.或使用
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.