如何使用 React Hooks 进行 fetch; ESLint 强制执行 `exhaustive-deps` 规则,这会导致无限循环

2024-05-29

总的来说,我对 React hooks 还很陌生,对 React hooks 也很陌生useSelector and useDispatch in react-redux,但我在执行一个简单的get当我的组件加载时请求。我想要get仅发生一次(当组件最初加载时)。我以为我知道如何做到这一点,但我遇到了 ESLint 问题,它阻止我执行我所理解的合法代码。

我有这个钩子,我试图抽象我的状态代码:

export const useState = () => {
  const dispatch = useDispatch();
  const data = useSelector((state) => state.data);

  return {
    data: data,
    get: (props) => dispatch(actionCreators.get(props))
  };
};

在上述函数的背后,有一个网络请求通过redux-saga and axios,并且已经在生产代码中运行了一段时间。到目前为止,一切都很好。现在我想在功能组件中使用它,所以我这样写:

import * as React from 'react';
import { useState } from './my-state-file';

export default () => {
  const myState = useState();

  React.useEffect(
    () => {
      myState.get();
      return () => {};
    },
    []
  );
  return <div>hello, world</div>;
};

我期望发生的是因为我的useEffect有一个空数组作为第二个参数,它只会执行一次,所以get当组件加载时就会发生,就是这样。

然而,我在 Atom 中保存时运行 ESLint,每次保存时,它都会改变那一秒[]论证是[myState],其结果是:

import * as React from 'react';
import { useState } from './my-state-file';

export default () => {
  const myState = useState();

  React.useEffect(
    () => {
      myState.get();
      return () => {};
    },
    [myState]
  );
  return <div>hello, world</div>;
};

如果我加载这个组件,那么 get 会运行每个渲染,这当然与我想要发生的情况完全相反。我在文本编辑器中打开了这个文件not在保存时运行 ESLint,所以当我能够保存时useEffect有一个空白[], 有效。

所以我很困惑。我的猜测是我上面使用的模式不正确,但我不知道“正确”的模式是什么。

任何帮助表示赞赏。

Thanks!

UPDATE:

根据罗伯特·库珀的回答和丹·阿布拉莫夫的链接文章,我做了一些更多的实验。我还没有完全做到这一点,但我设法让事情顺利进行。

最大的变化是我需要添加一个useCallback围绕我的调度功能,如下所示:

export const useState = () => {
  const dispatch = useDispatch();
  const data = useSelector((state) => state.data);
  const get = React.useCallback((props) => dispatch({type: 'MY_ACTION', payload:props}), [
    dispatch
  ]);

  return {
    data: data,
    get: get,
  };
};

我必须承认,我不完全理解为什么我需要 useCallback 那里,但它有效。

不管怎样,我的组件看起来像这样:

import * as React from 'react';
import { useState } from './my-state-file';

export default () => {
  const {get, data}  = useState();

  React.useEffect(
    () => {
      get();
      return () => {};
    },
    [get]
  );
  return <div>{do something with data...}</div>;
};

真正的代码有点复杂,我希望能够抽象出useEffect完全调出组件并将其放入useState自定义钩子,或从同一钩子导入的另一个钩子my-state-file file.


我相信您遇到的问题是myState您的依赖项数组中的值不同,或者在每次渲染时都有不同的 JavaScript 对象引用。解决这个问题的方法是传递一个记忆或缓存版本myState作为对你的依赖useEffect.

你可以尝试使用useMemo https://reactjs.org/docs/hooks-reference.html#usememo返回您的自定义状态返回的记忆版本useState。这可能看起来像这样:

export const useState = () => {
  const dispatch = useDispatch();
  const data = useSelector((state) => state.data);

  return useMemo(() => ({
    data: data,
    get: (props) => dispatch(actionCreators.get(props))
  }), [props]);
};

以下是丹·阿布拉莫夫 (Dan Abramov) 关于无限循环的说法useEffect方法:

问:为什么有时会出现无限重新获取循环?

如果您在没有第二个依赖项参数的情况下进行数据获取,则可能会发生这种情况。如果没有它,效果会在每次渲染后运行 - 并且设置状态将再次触发效果。如果您指定的值在依赖项数组中始终发生变化,则也可能会发生无限循环。你可以通过将它们一一删除来辨别是哪一个。但是,删除您使用的依赖项(或盲目指定 [])通常是错误的修复方法。相反,从根源上解决问题。例如,函数可能会导致此问题,将它们放入效果中、将它们提升出来或使用 useCallback 包装它们会有所帮助。为了避免重新创建对象,useMemo 可以起到类似的作用。

完整文章在这里:https://overreacted.io/a-complete-guide-to-useeffect/ https://overreacted.io/a-complete-guide-to-useeffect/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 React Hooks 进行 fetch; ESLint 强制执行 `exhaustive-deps` 规则,这会导致无限循环 的相关文章

随机推荐