简体   繁体   English

如何使用React Hooks进行获取; ESLint强制执行“穷举下降”规则,这会导致无限循环

[英]How to do fetch with React Hooks; ESLint enforcing `exhaustive-deps` rule, which causes infinite loop

I'm pretty new to React hooks in general, and very new to useSelector and useDispatch in react-redux , but I'm having trouble executing a simple get request when my component loads. 一般而言,我对React钩子还是很useSelectoruseDispatchreact-redux useSelectoruseDispatch ,我还是很新的,但是当我的组件加载时,我很难执行一个简单的get请求。 I want the get to happen only once (when the component initially loads). 我希望get仅发生一次(组件最初加载时)。 I thought I knew how to do that, but I'm running into an ESLint issue that's preventing me from doing what I understand to be legal code. 我以为我知道该怎么做,但是我遇到了一个ESLint问题,这使我无法做我理解为合法代码的事情。

I have this hook where I'm trying to abstract my state code: 我在尝试抽象状态代码的地方有这个钩子:

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

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

Behind the above function, there's a network request that happens via redux-saga and axios , and has been running in production code for some time. 在上述功能的背后,有一个网络请求通过redux-sagaaxios ,并且已经在生产代码中运行了一段时间。 So far, so good. 到现在为止还挺好。 Now I want to use it in a functional component, so I wrote this: 现在,我想在功能组件中使用它,所以我这样写:

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>;
};

What I expected to happen was that because my useEffect has an empty array as the second argument, it would only execute once, so the get would happen when the component loaded, and that's it. 我期望发生的是,因为我的useEffect有一个空数组作为第二个参数,所以它只会执行一次,因此在加载组件时便会发生get ,仅此而已。

However, I have ESLint running on save in Atom, and every time I save, it changes that second [] argument to be [myState] , the result of which is: 但是,我让ESLint在Atom的save上运行,并且每次保存时,它将第二个[]参数更改为[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>;
};

If I load this component, then the get runs every single render, which of course is the exact opposite of what I want to have happen. 如果加载此组件,则get将在每个渲染器中运行,这当然与我想要发生的情况完全相反。 I opened this file in a text editor that does not have ESLint running on save, so when I was able to save useEffect with a blank [] , it worked. 我在没有在保存时运行ESLint的文本编辑器中打开了该文件,因此当我能够使用空白[]保存useEffect时,它就可以了。

So I'm befuddled. 所以我很困惑。 My guess is the pattern I'm using above is not correct, but I have no idea what the "right" pattern is. 我的猜测是我上面使用的模式不正确,但是我不知道什么是“正确的”模式。

Any help is appreciated. 任何帮助表示赞赏。

Thanks! 谢谢!

UPDATE: 更新:

Based on Robert Cooper's answer, and the linked article from Dan Abramov, I did some more experimenting. 基于罗伯特·库珀的回答以及丹·阿布拉莫夫的链接文章,我做了一些更多的实验。 I'm not all the way there yet, but I managed to get things working. 我还没到那儿,但是我设法使事情顺利进行。

The big change was that I needed to add a useCallback around my dispatch functions, like so: 最大的变化是我需要在调度函数周围添加一个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,
  };
};

I must admit, I don't fully understand why I need useCallback there, but it works. 我必须承认,我不完全理解为什么在那里需要useCallback,但是它可以工作。

Anyway, then my component looks like this: 无论如何,然后我的组件如下所示:

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>;
};

The real code is a bit more complex, and I'm hoping to abstract the useEffect call out of the component altogether and put it into either the useState custom hook, or another hook imported from the same my-state-file file. 实际的代码要复杂一些,我希望从组件中抽象出useEffect调用,并将其放入useState自定义钩子,或从同一my-state-file文件导入的另一个钩子。

I believe the problem you're encountering is that the value of myState in your dependency array isn't the same value or has a different JavaScript object reference on every render. 我相信您遇到的问题是,依赖项数组中myState的值不是相同的值,或者在每个呈现器上都有不同的JavaScript对象引用。 The way to get around this would be to pass a memoized or cached version of myState as a dependency to your useEffect . 解决此问题的方法是将一个经过记忆或缓存的myState版本作为对myState的依赖项useEffect

You could try using useMemo to return a memoized version of your state return by your custom useState . 您可以尝试使用useMemo来通过自定义useState返回状态返回值的备注版本。 This might look something like this: 这可能看起来像这样:

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

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

Here's what Dan Abramov has to say regarding infinite loops in useEffect methods: 关于useEffect方法中的无限循环,这是Dan Abramov所说的:

Question: Why do I sometimes get an infinite refetching loop? 问题:为什么有时会出现无限重访循环?

This can happen if you're doing data fetching in an effect without the second dependencies argument. 如果您在没有第二个依赖项参数的情况下进行数据获取,则会发生这种情况。 Without it, effects run after every render — and setting the state will trigger the effects again. 如果没有它,效果将在每次渲染后运行-设置状态将再次触发效果。 An infinite loop may also happen if you specify a value that always changes in the dependency array. 如果您指定一个始终在依赖项数组中更改的值,则也可能会发生无限循环。 You can tell which one by removing them one by one. 您可以通过一一删除它们来分辨出哪一个。 However, removing a dependency you use (or blindly specifying []) is usually the wrong fix. 但是,删除您使用的依赖项(或盲目指定[])通常是错误的解决方案。 Instead, fix the problem at its source. 而是从根本上解决问题。 For example, functions can cause this problem, and putting them inside effects, hoisting them out, or wrapping them with useCallback helps. 例如,函数可能会导致此问题,并将其放入效果中,将其吊起或使用useCallback帮助进行包装。 To avoid recreating objects, useMemo can serve a similar purpose. 为了避免重新创建对象,useMemo可以达到类似的目的。

Full article here: https://overreacted.io/a-complete-guide-to-useeffect/ 全文在这里: https : //overreacted.io/a-complete-guide-to-useeffect/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 ESLint 希望 setSate 作为 useEffect 的依赖,但这会导致无限循环(react-hooks/exhaustive-deps) - ESLint wants setSate as a dependency for useEffect but this causes an infinite loop (react-hooks/exhaustive-deps) 如何在React中使用钩子实现componentDidMount以使其符合EsLint规则“ react-hooks / exhaustive-deps”:“ warn”? - How to implement componentDidMount with hooks in React to be in line with the EsLint rule “react-hooks/exhaustive-deps”: “warn”? 使用 i18next 时如何处理 ESLint react-hooks 'exhaustive-deps' 规则? - How to handle ESLint react-hooks 'exhaustive-deps' rule when using i18next? react-hooks/exhaustive-deps 警告还是无限循环? - react-hooks/exhaustive-deps warning or infinite loop? React Hooks Exhaustive-deps 异步无限循环 - React Hooks Exhaustive-deps async infinite Loop React 使用 function 使用 state 的异步钩子穷举无限循环 - React hooks exhaustive-deps infinite loop with async function which uses state React挂钩:如何使用react-hooks / exhaustive-deps规则在没有无限循环的情况下读取和更新挂钩中的状态 - React hooks: How to read & update state in hooks without infinite loops with react-hooks/exhaustive-deps rule React Hooks react-hooks/exhaustive-deps eslint 规则似乎过于敏感 - React Hooks react-hooks/exhaustive-deps eslint rule seems overly sensitive 我如何正确使用 useEffect 进行异步获取调用和反应? 反应钩子/详尽的deps - How do i properly use useEffect for a async fetch call with react? react-hooks/exhaustive-deps 如何使用 eslint 规则 react-hooks/exhaustive-deps 警告无点 function 表达式中的违规行为 - How to use eslint rule react-hooks/exhaustive-deps to warn about violations in point free function expressions
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM