开发者问题收集

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

2019-04-25
1167637

使用 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 规则 #14920 ,其中一个可能的解决方案是 如果您认为自己知道自己在做什么,您可以随时 // eslint-disable-next-line react-hooks/exhaustive-deps。 我对自己所做的事情没有信心,所以我还没有尝试实现它。

我有这个当前设置, 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
      });
  };
3个回答

如果您没有在效果之外的任何地方使用 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. 当在 mount 期间使用 fetchBusinesses 及其封闭闭包时,您 将该方法作为方法传递,这是否存在问题?
  2. 您的方法是否依赖于从其封闭闭包接收的一些变量?您不是这种情况。
  3. 在每次渲染时,fetchBusinesses 都会重新创建,因此将其传递给 useEffect 会导致问题。因此,如果要将 fetchBusinesses 传递给依赖项数组,则首先必须记住它。

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

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

useEffect(() => {
   // other code
   ...
 
   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 
Shubham Khatri
2019-04-25

如果您正在创建新应用或具有足够的灵活性,那么状态管理库有很多不错的选择。请查看 Recoil。

仅出于完整性考虑:

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

useEffect(fetchBusinesses, [])

2. 在 useEffect() 内声明函数

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

3.使用 useCallback()

进行记忆化

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

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

4.函数的默认参数

Behnam Azimi

It's not best practice, but it could be useful in some cases.

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

jpenna
2020-02-20
./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) 警告。

它告诉您该钩子依赖于函数 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

r g
2019-04-25