理解 React Hooks’彻底深度’棉绒规则

我很难理解“详尽深度”棉绒规则。

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

下面是一个带有 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>
)
}

它要求我将 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? (我是一个有钩子的菜鸟)

125027 次浏览

Linter 规则之所以希望 onChange进入 useEffect钩子,是因为 onChange可以在不同呈现之间进行更改,而 lint 规则旨在防止那种“过时数据”引用。

例如:

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


return <MyCustomComponent onChange={onChange} />
}

MyParentComponent的每一次渲染都会向 MyCustomComponent传递一个不同的 onChange函数。

在您的特定情况下,您可能并不关心: 您只想在值发生变化时调用 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>
)
}

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

exhaustive-deps警告的主要目的是防止开发人员在其效果中遗漏依赖项并丢失某些行为。

Dan Abramov Facebook 核心的开发者 强烈建议保持该规则处于启用状态

对于将函数作为依赖项传递的情况,在反应常见问题解答中有一个专门的章节:

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

博士

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

  • 将函数放在组件之外,这样可以确保在每次呈现时不会更改引用。
  • 如果可以,调用效果之外的函数,并将结果作为依赖项使用。
  • 如果函数必须在组件作用域中声明,则必须使用 useCallback挂钩来制表函数引用。只有当回调函数的依赖项发生更改时,才会更改引用。