了解 React Hooks 'exhaustive-deps' lint 规则

Log*_*Wlv 75 reactjs eslint react-hooks

我很难理解 'exhaustive-deps' lint 规则。

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

这是一个带有 lint 问题的简单 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>
    )
} 
Run Code Online (Sandbox Code Playgroud)

它需要我添加onChangeuseEffect依赖项数组。但在我的理解中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> ?
    )
} 
Run Code Online (Sandbox Code Playgroud)

为什么是棉绒?关于第一个示例的 lint 规则的任何明确解释?

或者我不应该useEffect在这里使用?(我是一个有钩子的菜鸟)

Ret*_*sam 78

linter 规则想要onChange进入useEffect钩子的原因是因为可以onChange在渲染之间进行更改,而 lint 规则旨在防止这种“陈旧数据”引用。

例如:

const MyParentComponent = () => {
    const onChange = (value) => { console.log(value); }

    return <MyCustomComponent onChange={onChange} />
}
Run Code Online (Sandbox Code Playgroud)

每个渲染MyParentComponent都会将不同的onChange函数传递给MyCustomComponent.

在您的特定情况下,您可能并不关心:您只想onChange 在值更改时调用,而不是在onChange函数更改时调用。但是,从您如何使用useEffect.


这里的根源是你useEffect有点单调。

useEffect最适合用于副作用,但在这里您将其用作一种“订阅”概念,例如:“当 Y 更改时执行 X”。由于deps数组的机制,这确实在功能上起作用(尽管在这种情况下您还调用onChange了初始渲染,这可能是不需要的),但这不是预期的目的。

调用onChange真的不是一个副作用这里,它只是触发的效果onChange的事件<input>。所以,我认为你调用这两个第二个版本onChange,并setValue共同为更地道。

如果有其他设置值的方法(例如清除按钮),不断地记住调用onChange可能会很乏味,所以我可以这样写:

const MyParentComponent = () => {
    const onChange = (value) => { console.log(value); }

    return <MyCustomComponent onChange={onChange} />
}
Run Code Online (Sandbox Code Playgroud)

但在这一点上,这是令人毛骨悚然的。

  • 这是一个很好的解释。我还使用“useEffect”作为订阅工件。这里唯一仍然让我困惑的问题是:如果我还想在组件加载时触发第一个“onChange”怎么办?那么,“useEffect”对于“[]”依赖项就有意义了。但随后,lint 又会抱怨。 (8认同)

Fre*_*eez 46

exhaustive-deps警告的主要目的是防止开发人员在他们的 effect 中丢失依赖项并丢失一些行为。

Dan abramov – Facebook 核心开发人员 –强烈建议保持该规则启用

对于将函数作为依赖项传递的情况,React FAQ 中有一个专门的章节

https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies

tl;博士

如果您必须在依赖项数组中放置一个函数:

  • 将函数放在组件之外,这样您就可以确保每次渲染时都不会更改引用。
  • 如果可以,请在效果之外调用该函数,并将结果用作依赖项。
  • 如果必须在组件范围内声明函数,则必须使用useCallback钩子记住函数引用。只有当回调函数的依赖关系发生变化时,引用才会发生变化。

  • @IsaacPak 完全同意。从这个意义上说,React 过于复杂。 (5认同)
  • 为什么我们不能只将该函数添加为依赖项,而不需要所有 useCallback 开销? (3认同)