如何使用React Hooks进行获取; ESLint强制执行“穷举-下降"规则,这会导致无限循环 [英] How to do fetch with React Hooks; ESLint enforcing `exhaustive-deps` rule, which causes infinite loop

查看:76
本文介绍了如何使用React Hooks进行获取; ESLint强制执行“穷举-下降"规则,这会导致无限循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一般来说,我对React钩子还是很陌生的,对于react-redux中的useSelectoruseDispatch来说还是很新的,但是在加载组件时执行简单的get请求时遇到了麻烦.我希望get仅发生一次(组件最初加载时).我以为我知道该怎么做,但是我遇到了一个ESLint问题,这使我无法做我理解为合法代码的事情.

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. I want the get to happen only once (when the component initially loads). 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.

我在尝试抽象状态代码的地方有这个钩子:

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

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

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

我希望发生的是,因为我的useEffect有一个空数组作为第二个参数,它只会执行一次,所以get会在组件加载时发生,仅此而已.

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.

但是,我让ESLint在Atom的save中运行,并且每次保存时,它将第二个[]参数更改为[myState],其结果是:

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:

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将在每个渲染中运行,这当然与我想要发生的情况完全相反.我在一个文本编辑器中打开了这个文件,该编辑器在保存时没有运行ESLint,所以当我能够用空白的[]保存useEffect时,它就起作用了.

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. 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.

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

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.

感谢您的帮助.

谢谢!

更新:

基于罗伯特·库珀(Robert Cooper)的回答以及丹·阿布拉莫夫(Dan Abramov)的链接文章,我做了一些更多的实验.我还没到那儿,但我设法使事情正常了.

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.

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

The big change was that I needed to add a useCallback around my dispatch functions, like so:

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,但是它可以工作.

I must admit, I don't fully understand why I need useCallback there, but it works.

无论如何,我的组件看起来像这样:

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

真正的代码要复杂一些,我希望从组件中完全提取useEffect调用,并将其放入useState自定义钩子或从同一文件.

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.

推荐答案

我相信您遇到的问题是依赖数组中myState的值不是相同的值或具有不同的JavaScript对象引用在每个渲染上.解决此问题的方法是将对myState的记忆或缓存版本作为对useEffect的依赖项传递.

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. The way to get around this would be to pass a memoized or cached version of myState as a dependency to your useEffect.

您可以尝试使用 useMemo 来返回已记录的版本您的自定义useState返回状态.这可能看起来像这样:

You could try using useMemo to return a memoized version of your state return by your custom 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]);
};

这是Dan Abramov关于useEffect方法中的无限循环的内容:

Here's what Dan Abramov has to say regarding infinite loops in useEffect methods:

问题:为什么有时会出现无限重访循环?​​

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

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. To avoid recreating objects, useMemo can serve a similar purpose.

此处全文: https://overreacted.io/a-complete-使用效果指南/

这篇关于如何使用React Hooks进行获取; ESLint强制执行“穷举-下降"规则,这会导致无限循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆