更好的 React Custom Hooks 结构可以避免 exhaustive-deps 警告?

Better Structure for React Custom Hooks that avoid `exhaustive-deps` warning?

我注意到 React 的 exhaustive-deps 行规则并不总是与 setState 函数或当您抽象出 customHooks 时很好地发挥作用。

例如,如果我有一个像这样的 customHook:

function useValidation(initialThings) {
  const [needsValidation, setNeedsValidation] = useState(false);
  const [thingsToValidate, setThingsToValidate] = useState<Things[]>(initialThings);

  useEffect(() => {
    debounceValidateThings(thingsToValidate);
  }, [things, debounceValidateThings]);

  return {
    needsValidation,
    setNeedsValidation,
    thingsToValidate,
    setThingsToValidate,
  }
}

我在挂钩之外使用了其中一个 setState 函数:

 const validationHook = useValidation(initialThings)


 useEffect(() => {
    // Add something to validate
    validationHook.setThingsToValidate(newThings)
    validationHook.setNeedsValidation(true)
  }, [newThings]);

我注意到这个 returns 带有详尽细节的警告。它建议我将整个 useValidation 添加到可能导致过度重新渲染的深度。

同时,它不会让我只添加 setState 调用:例如setThingsToValidatesetNeedsValidation

仍然建议:

  1. setState 被添加为 dep,即使它不一定是 (https://reactjs.org/docs/hooks-reference.html#usestate)
  2. 它建议将整个 useX 挂钩添加到 deps 而不是 useX.setState 这会导致不必要的重新渲染

有没有不涉及 lint 警告的解决方法?或者这里有一个范例来抽象出我缺少的钩子??

这里是:

  return {
    needsValidation,
    setNeedsValidation,
    thingsToValidate,
    setThingsToValidate,
  }

每次挂钩运行时创建一个新对象。然后,当您将该对象用作挂钩依赖项时:

 useEffect(() => {
    // Add something to validate
    validationHook.setThingsToValidate(newThings)
    validationHook.setNeedsValidation(true)
  }, [newThings, validationHook]);

钩子在每次渲染时都会失效。将此对象列为依赖项是绝对正确的,因为它一个依赖项。

所以你有几个选择。


您可以记忆返回的对象:

return useMemo(() => ({
  needsValidation,
  setNeedsValidation,
  thingsToValidate,
  setThingsToValidate,
}, [
  needsValidation,
  thingsToValidate,
  // eslint should know state setters are stable in this scope
  // so they can be omitted
])

现在,除非状态发生变化,否则返回对象的身份将得到保留。这使得使用效果依赖性变得安全。


但我认为更好的方法是解构返回的对象,这样您就可以使用 属性 值。状态设置器应该是稳定的,任何依赖于状态值的东西都需要更新。

const {
  needsValidation,
  setNeedsValidation,
  thingsToValidate,
  setThingsToValidate,
} = useValidation(initialThings)

useEffect(() => {
  setThingsToValidate(newThings)
  setNeedsValidation(true)
}, [newThings, setThingsToValidate, setNeedsValidation]);

或者您可以将返回对象的属性列为挂钩依赖项:

const validationHook = useValidation(initialThings)

useEffect(() => {
  // Add something to validate
  validationHook.setThingsToValidate(newThings)
  validationHook.setNeedsValidation(true)
}, [
  newThings,
  validationHook.setThingsToValidate,
  validationHook.setNeedsValidation,
]);