如何在React中为自定义钩子处理依赖项数组

时间:2019-05-22 17:59:20

标签: reactjs react-hooks

我正在创建一个自定义钩子,并且想定义一个可选参数,以便在需要时可以传递额外的依赖关系。我的代码如下所示:

import { useEffect } from 'react';

function useCustomHook(param1, extraDeps) {
  useEffect(() => {
    // do something with param1 here
  }, [param1, ...extraDeps])
}

react-hooks / exhaustive-deps发出警告说

  

React Hook useEffect在其依赖项数组中具有一个split元素。这意味着我们无法静态验证您是否已经传递了正确的依赖项

任何人都知道如何解决该警告?还是将deps数组传递给自定义钩子不是一个好习惯?

对于那些对为什么需要extraDeps感兴趣的人。这是一个例子:

const NewComponent = (props) => {
  [field1, setField1] = useState()
  [field2, setField2] = useState()

  // I only want this to be called when field1 change
  useCustomHook('.css-selector', [field1]);

  return <div>{field1}{field2}</div>;
}

6 个答案:

答案 0 :(得分:3)

如果您想提供额外的提示,则可以使用useDeepCompareEffect代替useEffect

https://github.com/kentcdodds/use-deep-compare-effect

答案 1 :(得分:1)

我找到了一个有用的替代方案来替代这里提出的解决方案。正如前面提到的 in this Reddit topic,React 团队显然推荐了以下内容:

// Pass the callback as a dep
cost useCustomHook = callback => {
  useEffect(() => { /* do something */ }, [callback])
};

// Then the user wraps the callback in `useMemo` to avoid running the effect too often
// Whenever the deps change, useMemo will ensure that the callback changes, which will cause effect to re-run
useCustomHook(
  useMemo(() => { /* do something }, [a, b, c])
);

我使用过这种技术并且效果很好。

答案 2 :(得分:0)

这是你可以做的:

将状态移到您的自定义钩子上,对其运行效果并返回它。

类似的东西:

Component.js


function Component() {
  const [field,setField] = useCustomHook(someProps);
}

useCustomHook.js

import {useState, useEffect} from 'react';

function useCustomHook(props) {

  const [field,setField] = useState('');

  useEffect(()=>{
    // Use props received and perform effect after changes in field 1
  },[field1]);

  return([
    field,
    setField
  ]);
}

答案 3 :(得分:0)

我认为问题在于您如何在自定义钩子上创建依赖项数组。每次执行[param1, ... extraDeps]时,您都在创建一个新的数组,因此React总是将它们视为不同的。

尝试将您的自定义钩子更改为:

function useCustomHook(deps) {
  useEffect(() => {
    // do something with param1 here
  }, deps)
}

然后像

一样使用它
const NewComponent = (props) => {
  [field1, setField1] = useState()
  [field2, setField2] = useState()

  // I only want this to be called when field1 change
  useCustomHook(['.css-selector', field1]);

  return <div>{field1}{field2}</div>;
}

希望有帮助!

答案 4 :(得分:0)

我有一个类似的问题,我希望每当更改了一些额外的依赖项时就执行一种效果。
我没有设法提供那些额外的依赖关系,而是通过给调用方我想执行的回调并让他在需要时使用它来解决了问题。

示例:

// This hook uses extraDeps unknown by EsLint which causes a warning
const useCustomEffect = (knowDep, extraDeps) => {

  const doSomething = useCallback((knowDep) => {/**/}, [])

  useEffect(() => {
    doSomething(knowDep)
  }, [doSomething, knowDep, ...extraDeps]) // Here there is the warning
}

//Instead of this, we give the caller the callback
const useCustomEffect = (knowDep) => {

  const doSomething = useCallback((knowDep) => {/**/}, [])

  useEffect(() => {
    doSomething(knowDep)
  }, [doSomething, knowDep]) // no more warning

  return { doSomething }
}

// Use it like this
const { doSomething } = useCustomEffect(foo)
useEffect(doSomething, [bar, baz]) // know I can use my callback for any known dependency

答案 5 :(得分:0)

您定义自定义钩子的方式对我来说很有意义。我无法找到任何有关执行此操作的官方方法的文档,因此现在我的解决方案是禁用该规则:

function useCustomHook(param1, extraDeps) {
  useEffect(() => {
    // do something with param1 here
  }, [param1, ...extraDeps]) // eslint-disable-line react-hooks/exhaustive-deps
}