理解React Hooks的``穷尽-下降''棉绒规则 [英] Understanding the React Hooks 'exhaustive-deps' lint rule
问题描述
我很难理解穷尽"的皮棉规则.
这是一个带有皮棉问题的简单React组件:
const MyCustomComponent = ({onChange}) => {
const [value, setValue] = useState('');
useEffect(() => {
onChange(value);
}, [value]);
return (
<input
value={value}
type='text'
onChange={(event) => setValue(event.target.value)}>
</input>
)
}
需要我将onChange
添加到useEffect
依赖项数组.但根据我的理解,onChange
永远不会改变,因此它不应该存在.
通常我是这样管理的:
const MyCustomComponent = ({onChange}) => {
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
onChange(event.target.value)
}
return (
<input value={value} type='text' onChange={handleChange}></input>
)
}
为什么皮棉?关于第一个示例的皮棉规则有任何清楚的解释吗?
还是我不应该在这里使用useEffect
? (我是一个带钩的菜鸟)
linter规则希望onChange
进入useEffect
挂钩的原因是因为onChange
可能在渲染和皮棉之间进行更改规则旨在防止此类陈旧数据"引用.
例如:
const MyParentComponent = () => {
const onChange = (value) => { console.log(value); }
return <MyCustomComponent onChange={onChange} />
}
MyParentComponent
的每个单个渲染器都会将不同的onChange
函数传递给MyCustomComponent
.
在您的特定情况下,您可能不在乎:您只想在值更改时调用onChange
,而不是在onChange
函数更改时调用.但是,从useEffect
的使用方式尚不清楚.
这里的根源是您的useEffect
有点单调.
useEffect
最适合用于副作用,但是在这里您将其用作一种订阅"概念,例如:当Y改变时执行X".由于deps
数组的机制,该功能在功能上确实可以工作(尽管在这种情况下,您还会在初始渲染时调用onChange
,这可能是不必要的),但这不是预期的目的.>
在这里调用onChange
确实不是副作用,它只是触发<input>
的onChange
事件的效果.因此,我确实认为您同时调用onChange
和setValue
的第二个版本更惯用了.
如果还有其他设置值的方法(例如,清除按钮),则经常不得不记住调用onChange
可能很乏味,因此我可以这样写:
const MyCustomComponent = ({onChange}) => {
const [value, _setValue] = useState('');
// Always call onChange when we set the new value
const setValue = (newVal) => {
onChange(newVal);
_setValue(newVal);
}
return (
<input value={value} type='text' onChange={e => setValue(e.target.value)}></input>
<button onClick={() => setValue("")}>Clear</button>
)
}
但是在这一点上,这是头发劈开.
I'm having a hard time understanding the 'exhaustive-deps' lint rule.
I already read this post and this post but I could not find an answer.
Here is a simple React component with the lint issue:
const MyCustomComponent = ({onChange}) => {
const [value, setValue] = useState('');
useEffect(() => {
onChange(value);
}, [value]);
return (
<input
value={value}
type='text'
onChange={(event) => setValue(event.target.value)}>
</input>
)
}
It requires me to add onChange
to the useEffect
dependencies array. But in my understanding onChange
will never change, so it should not be there.
Usually I manage it like this:
const MyCustomComponent = ({onChange}) => {
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
onChange(event.target.value)
}
return (
<input value={value} type='text' onChange={handleChange}></input>
)
}
Why the lint? Any clear explanation about the lint rule for the first example?
Or should I not be using useEffect
here? (I'm a noob with hooks)
The reason the linter rule wants onChange
to go into the useEffect
hook is because it's possible for onChange
to change between renders, and the lint rule is intended to prevent that sort of "stale data" reference.
For example:
const MyParentComponent = () => {
const onChange = (value) => { console.log(value); }
return <MyCustomComponent onChange={onChange} />
}
Every single render of MyParentComponent
will pass a different onChange
function to MyCustomComponent
.
In your specific case, you probably don't care: you only want to call onChange
when the value changes, not when the onChange
function changes. However, that's not clear from how you're using useEffect
.
The root here is that your useEffect
is somewhat unidiomatic.
useEffect
is best used for side-effects, but here you're using it as a sort of "subscription" concept, like: "do X when Y changes". That does sort of work functionally, due to the mechanics of the deps
array, (though in this case you're also calling onChange
on initial render, which is probably unwanted), but it's not the intended purpose.
Calling onChange
really isn't a side-effect here, it's just an effect of triggering the onChange
event for <input>
. So I do think your second version that calls both onChange
and setValue
together is more idiomatic.
If there were other ways of setting the value (e.g. a clear button), constantly having to remember to call onChange
might be tedious, so I might write this as:
const MyCustomComponent = ({onChange}) => {
const [value, _setValue] = useState('');
// Always call onChange when we set the new value
const setValue = (newVal) => {
onChange(newVal);
_setValue(newVal);
}
return (
<input value={value} type='text' onChange={e => setValue(e.target.value)}></input>
<button onClick={() => setValue("")}>Clear</button>
)
}
But at this point this is hair-splitting.
这篇关于理解React Hooks的``穷尽-下降''棉绒规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!