使用useEffect React Hook时如何修复缺少的依赖警告

IT技术 reactjs eslint create-react-app react-hooks
2021-03-23 23:22:36

使用 React 16.8.6(在以前的版本 16.8.3 上很好),当我尝试防止获取请求上的无限循环时出现此错误:

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

我一直无法找到停止无限循环的解决方案。我想远离使用useReducer(). 我确实找到了这个讨论[ESLint] 'exhaustive-deps' lint rule #14920 的反馈,其中一个可能的解决方案是You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.我对自己正在做的事情没有信心,所以我还没有尝试实施它。

我有这个当前设置,React hook useEffect 永远/无限循环持续运行,唯一的评论是useCallback()我不熟悉的。

我目前的使用方式useEffect()(我只想在开始时运行一次,类似于componentDidMount()):

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
6个回答

如果您没有在效果之外的任何地方使用 fetchBusinesses 方法,您可以简单地将其移动到效果中并避免警告

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

但是,如果您在渲染之外使用 fetchBusinesses,则必须注意两件事

  1. 当它在带有封闭闭包的挂载期间使用时,您没有将其fetchBusinesses作为方法传递是否有任何问题
  2. 你的方法是否依赖于它从封闭闭包中接收到的一些变量?这不是你的情况。
  3. 在每次渲染时,将重新创建 fetchBusinesses,因此将其传递给 useEffect 会导致问题。因此,如果要将 fetchBusinesses 传递给依赖项数组,则首先必须记住它。

总而言之,我会说,如果您在fetchBusinesses外部使用,则useEffect可以禁用规则 using// eslint-disable-next-line react-hooks/exhaustive-deps否则您可以将方法移动到 useEffect 内部

要禁用规则,您可以像这样写

useEffect(() => {
   // other code
   ...

   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 
@russ,您是对的,如果要将 fetchBusiness 传递给依赖项数组,则需要使用 useCallback 记住它
2021-05-22 23:22:36
我使用了您很好地概述的解决方案。由于不同的设置,我在其他地方使用的另一个解决方案是useCallback(). 例如: const fetchBusinesses= useCallback(() => { ... }, [...]) 并且useEffect()看起来像这样:useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
2021-05-23 23:22:36
使用// eslint-disable-next-line react-hooks/exhaustive-deps说明的棉短绒,你的代码是正确的就像是一个黑客。我希望他们会找到另一种解决方案,使 linter 足够智能,以检测何时不需要参数
2021-06-06 23:22:36
@TapasAdhikary,是的,您可以在 useEffect 中有一个异步函数,您只需要以不同的方式编写它。请查看stackoverflow.com/questions/53332321/...
2021-06-06 23:22:36
今天的 linter 仍然是愚蠢的,如果您在使用外部变量时想要类似 componentDidMount 的行为(如果它们发生变化,需要一些但不是全部来触发重新渲染),无论您做什么,您都会收到该警告......在至少我无法在网上找到解决方案
2021-06-10 23:22:36

编辑 09/20/2021

如果您正在创建新应用程序或有足够的灵活性,那么状态管理库有非常好的选择。看看反冲。

只是为了完整性:

1. (停止工作)使用函数作为useEffect回调

useEffect(fetchBusinesses, [])

### 2.在`useEffect()`中声明函数
useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3. 记忆 useCallback()

在这种情况下,如果您的函数中有依赖项,则必须将它们包含在useCallback依赖项数组中,useEffect如果函数的参数发生更改,这将再次触发此外,它是很多样板......所以只需将函数直接传递给useEffectas in 1. useEffect(fetchBusinesses, [])

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. 函数的默认参数

正如https://stackoverflow.com/a/61183162/5552686所建议的

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

5.创建自定义钩子

创建一个自定义钩子并在您只需要运行一次函数时调用它。它可能更干净。您还可以在需要时返回回调以重置重新运行“初始化”。

// customHooks.js
const useInit = (callback, ...args) => {
  const [mounted, setMounted] = useState(false)
  
  const resetInit = () => setMounted(false)

  useEffect(() => {
     if(!mounted) {
        setMounted(true);
        callback(...args);
     }
  },[mounted, callback]);

  return [resetInit]
}

// Component.js
return ({ fetchBusiness, arg1, arg2, requiresRefetch }) => {
  const [resetInit] = useInit(fetchBusiness, arg1, arg2)

  useEffect(() => {
    resetInit()
  }, [requiresRefetch, resetInit]);

6.禁用eslint的警告

禁用警告应该是你最后的手段,但是当你这样做时,最好内联和明确地这样做,因为未来的开发人员可能会在不知道 linting 关闭的情况下感到困惑或创建意外的错误

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps

原回复

您可以直接将其设置为useEffect回调:

useEffect(fetchBusinesses, [])

它只会触发一次,因此请确保正确设置了所有函数的依赖项(与 using 相同componentDidMount/componentWillMount...

第一个建议并没有令人惊讶地消除警告
2021-05-26 23:22:36
我更喜欢禁用警告作为最后的手段,因为未来的开发人员可能会在不知道 linting 关闭的情况下感到困惑或创建意外错误
2021-06-04 23:22:36
在最新的反应中。如果我声明一个状态并且该组件具有 useEffect 则显示警告。所以在这种情况下,解决方案 1,2 和 3 不适用,因为我希望它只运行一次。我该怎么办?
2021-06-04 23:22:36
useEffect(fetchBusinesses, [])将抛出“TypeError:除了用于清理的函数之外,效果函数不得返回任何内容。”因为fetchBusinesses返回一个承诺。
2021-06-10 23:22:36
取消 eslint 的警告可以吗?
2021-06-12 23:22:36
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

这不是 JavaScript/React 错误,而是ESLint (eslint-plugin-react-hooks) 警告。

它告诉您钩子依赖于 function fetchBusinesses,因此您应该将其作为依赖项传递。

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

如果函数在组件中声明,则可能导致在每次渲染时调用该函数,例如:

const Component = () => {
  /*...*/

  // New function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

因为每次使用新引用重新声明该函数时。

做这些事情的正确方法是:

const Component = () => {
  /*...*/

  // Keep the function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* Additional dependencies */])

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

或者只是在useEffect.

更多:[ESLint] 'exhaustive-deps' lint 规则的反馈 #14920

解决方案很好,如果在函数上修改另一个状态,则必须添加依赖项以避免另一个意外行为
2021-05-26 23:22:36

React 也给出了解决方案。他们建议您使用useCallback它将返回您的函数记忆版本:

'fetchBusinesses' 函数使 useEffect Hook(在第 NN 行)的依赖项在每次渲染时发生变化。要解决此问题,请将 'fetchBusinesses' 定义包装到它自己的 useCallback() 钩子 react-hooks/exhaustive-deps 中

useCallback使用简单,因为它具有与useEffect. 不同之处在于 useCallback 返回一个函数。它看起来像这样:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* Some stuff */ })
    .catch(() => { /* Some error handling */ })
  }, [/* deps */])
  // We have a first effect that uses fetchBusinesses
  useEffect(() => {
    // Do things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);
   // We can have many effects that use fetchBusinesses
  useEffect(() => {
    // Do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);
就我而言,这个useCallBack钩子解决了我的问题。要查看详细信息,请访问文档
2021-06-19 23:22:36

这些警告对于查找不一致更新的组件非常有帮助:从依赖项列表中省略函数是否安全?.

但是,如果您想删除整个项目中的警告,您可以将其添加到您的 ESLint 配置中:

  {
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/exhaustive-deps": 0
    }
  }