理解React Hooks的``穷尽-下降''棉绒规则 [英] Understanding the React Hooks 'exhaustive-deps' lint rule

查看:118
本文介绍了理解React Hooks的``穷尽-下降''棉绒规则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难理解穷尽"的皮棉规则.

我已经阅读了这篇文章这篇文章,但我找不到答案.

这是一个带有皮棉问题的简单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事件的效果.因此,我确实认为您同时调用onChangesetValue的第二个版本更惯用了.

如果还有其他设置值的方法(例如,清除按钮),则经常不得不记住调用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆