使用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()。我确实在https://github.com/facebook/react/issues/14920找到了这个讨论,其中可能的解决方法是You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.我对正在做的事情不确定,所以我还没有尝试实现它。 />我已经有了这个当前的设置React钩子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
      });
  };


#1 楼

如果除了效果之外没有在其他地方使用fetchBusinesses方法,则可以将其移至效果中并避免出现警告

渲染时,必须注意两件事


在将其与封闭包一起安装时使用fetchBusinesses作为方法是否存在问题?
您的方法是否取决于它从封闭的封闭中收到的一些变量?情况并非如此。
在每个渲染上,都会重新创建fetchBusinesses,因此将其传递给useEffect会引起问题。所以首先,如果要将fetchBusinesses传递给依赖项数组,则必须记住它。

总结一下,我要说的是,如果您在fetchBusinesses之外使用useEffect,则可以使用// eslint-disable-next-line react-hooks/exhaustive-deps禁用该规则,否则可以将方法移到useEffect内

要禁用该规则,您可以将其编写为

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();
}, []);


评论


我很好地使用了您概述的解决方案。由于设置不同,我在其他地方使用的另一个解决方案是useCallCallback()。因此,例如:const fetchBusinesses = useCallback(()=> {...},[...]),而useEffect()看起来像这样:useEffect(()=> {fetchBusinesses();},[fetchBusinesses ]);

– russ
19-4-25在22:39



@russ,您是对的,如果要将其传递给依赖项数组,则需要使用useCallback来记住fetchBusiness

–Shubham Khatri
19年4月26日在5:41

如果您显示将eslint-disable语句放在哪里,那将很好。我认为这将超过useEffect

–user210757
19年6月18日在21:30

使用// eslint-disable-next-line react-hooks / exhaustive-deps向linter解释您的代码正确无异。我希望他们会找到另一种解决方案,使短毛绒变得更聪明,以检测何时不是强制性的参数

–奥利维尔·布瓦西
19-10-27在11:59



@TapasAdhikary,是的,您可以在useEffect中使用异步函数,您只需要以不同的方式编写它即可。请检查stackoverflow.com/questions/53332321/…

–Shubham Khatri
19年11月20日在5:19

#2 楼

您可以将其直接设置为useEffect回调:
 useEffect(fetchBusinesses, [])
 

它将仅触发一次,因此请确保所有函数的依赖项都被触发。正确设置(与使用componentDidMount/componentWillMount...相同)

编辑02/21/2020
出于完整性考虑:
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 。禁用eslint的警告
useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps


评论


禁用eslint警告可以吗?

– Rohan Shenoy
9月2日下午13:48

我宁愿禁用警告作为万不得已的方法,因为将来的开发人员可能会感到困惑或创建意想不到的错误而又不知道掉毛了

– jpenna
9月2日18:12

在最新的反应。如果我声明一个状态并且该组件具有useEffect,则会显示警告。因此,在这种情况下,解决方案1,2和3不适用,因为我希望它只能运行一次。我该怎么办?

– Rohan Shenoy
9月3日4:32



@RohanShenoy如果我理解正确,您想在useEffect中使用状态,那是完全不同的情况。在这种情况下,您只能禁用警告,因为现在您有了第二个外部依赖项

– jpenna
9月4日15:36

useEffect(fetchBusinesses,[])将抛出“ TypeError:效果函数除用于清理的函数外不得返回任何其他内容。”因为fetchBusinesses返回了promise。

– Emile Bergeron
12月16日19:49

#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


这不是JS / 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 function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* additional dependencies */]) 

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

  /*...*/
}


或仅在useEffect中定义函数

更多:https://github.com/facebook/react/issues/14920

评论


解决方案很好,如果在函数上修改其他状态,则必须添加依赖项,以避免其他意外行为

–塞萨尔森
1月9日19:12

#4 楼

该解决方案也由react提供,他们建议您使用useCallback,这将返回函数的备注版本:

'fetchBusinesses'函数使useEffect Hook的依赖项(在NN行) )在每个渲染上进行更改。要解决此问题,请将'fetchBusinesses'定义包装到其自己的useCallback()中。钩子react-hooks / exhaustive-deps


区别在于useCallback返回一个函数。
看起来像这样:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* some stuff */ })
    .catch(() => { /* some error handling */ })
  }, [/* deps */])
  // We have a first effect thant uses fetchBusinesses
  useEffect(() => {
    // do things and then fetchBusinesses
    fetchBusinesses(); 
  }, [fetchBusinesses]);
   // We can have many effect thant uses fetchBusinesses
  useEffect(() => {
    // do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);


#5 楼

const [mount, setMount] = useState(false)
const fetchBusinesses = () => { 
   //function defination
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses]);


这是非常简单的解决方案,您不需要覆盖es-lint警告。只需维护一个标志来检查组件是否已安装。

评论


当您需要componentDidMount时,您将每次执行此操作?

–耶夫科·杰列夫(Jivko Jelev)
12月14日上午10:29

#6 楼

只需将函数作为参数传递给useEffect的数组即可。
useEffect(() => {
   functionName()
}, [functionName])


#7 楼

您尝试用这种方式
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
      });
  };


useEffect(() => {
    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();
      }, []);

如果您基于特定ID获取数据,则添加回调useEffect [id],然后无法向您显示警告
React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array

#8 楼

本文是使用钩子获取数据的很好的入门文章:https://www.robinwieruch.de/react-hooks-fetch-data/

本质上,请在useEffect内包含fetch函数定义:

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);


#9 楼

您可以删除第二个参数类型数组[],但每次更新也会调用fetchBusinesses()。如果愿意,可以在IF实现中添加fetchBusinesses()语句。

React.useEffect(() => {
  fetchBusinesses();
});


另一个是在组件外部实现fetchBusinesses()函数。只是不要忘记将任何依赖关系参数传递给您的fetchBusinesses(dependency)调用(如果有)。

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

function YourComponent (props) {
  const { fetch } = props;

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

  // ...
}


#10 楼

这些警告对于查找不一致更新的组件非常有帮助:https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-依赖项。

但是,如果要在整个项目中删除警告,则可以将其添加到eslint配置中:

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


#11 楼


我只想在开始时运行一次[fetchBusinesses],类似于componentDidMount()


您可以将fetchBusinesses完全拉出组件:

const fetchBusinesses = () => { // or pass some additional input from component as args
  return fetch("theURL", { method: "GET" }).then(n => process(n));
};

const Comp = () => {
  React.useEffect(() => {
    fetchBusinesses().then(someVal => {
      // ... do something with someVal
    });
  }, []); // eslint warning solved!
  return <div>{state}</div>;
};


这不仅可以提供简单的解决方案,而且可以解决详尽的下降警告。现在可以更好地测试fetchBusiness并简化Comp,因为它位于React树之外的模块范围内。

fetchBusinesses外部定位在这里可以很好地工作,因为我们只能从中读取初始props和状态无论如何,由于过时的关闭范围([]中的useEffect dep)而导致组件失效。

如何忽略函数依赖项将函数移到组件外-(我们正在使用此函数)
在渲染过程中调用函数,并让useEffect依赖于此值(纯计算函数)
添加该函数以影响deps并最后将其与useCallback包装在一起resort


关于其他解决方案:

如果您访问fetchBusinesses中的其他状态,则将useEffect()拉入q4312079q并没有帮助。 eslint仍然会抱怨:Codesandbox。

我也会避免eslint详尽无遗地忽略注释。在对依赖项进行一些重构和检查时,很容易忘记它们。

评论


我不了解“将功能移到组件外部”以摆脱依赖项警告。

–stahlmanDesign
8月24日23:22

#12 楼

好吧,如果您想换个角度看,您只需要知道React有exhaustive-deps的哪些选项?您不应该在效果内使用闭包函数的原因之一是在每个渲染上,它将再次被重新创建/销毁。
因此,在钩子中有多个React方法被认为是稳定的且不会耗尽您不必应用到useEffect依赖项,因此也不会违反react-hooks/exhaustive-deps的规则参与。例如,作为函数的useReduceruseState的第二个返回变量。
const [,dispatch] = useReducer(reducer, {});

useEffect(() => {
    dispatch(); // non-exhausted, eslint won't nag about this
}, []);

因此,您可以将所有外部依赖关系与当前依赖关系共存于化简函数中。
const [,dispatch] = useReducer((current, update) => {
    const { foobar } = update;
    // logic

    return { ...current, ...update };
}), {});

const [foobar, setFoobar] = useState(false);

useEffect(() => {
    dispatch({ foobar }); // non-exhausted `dispatch` function
}, [foobar]);


#13 楼

实际上,使用钩子进行开发时,警告非常有用。但在某些情况下,它可能会刺穿您。特别是当您不需要侦听依赖项更改时。

如果您不想将fetchBusinesses放在钩子的依赖项中,则只需将其作为参数传递给钩子的回调并设置main fetchBusinesses像这样的默认值

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


这不是最佳做法,但可以在某些情况下很有用。

也如Shubnam所写,您可以添加以下代码来告诉ESLint忽略对钩子的检查。

// eslint-disable-next-line react-hooks/exhaustive-deps


#14 楼

只需在下一行禁用eslint;

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);


这样,您就可以像使用组件一样安装它(称为一次)

已更新



const fetchBusinesses = useCallback(() => {
 // your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]); 


每当someDeps更改时,fetchBusinesss都会被调用

评论


而不是禁用它,只需执行以下操作:[fetchBusinesses]将自动删除警告,从而为我解决了该问题。

– Rotimi-best
19年6月3日在7:33

@RotimiBest-这样做会导致无限次重新渲染,如问题中所述

–user210757
19年6月18日在21:43

实际上,我前一段时间在我的一个项目中以这种方式进行了处理,但它没有产生无限循环。我会再次检查。

– Rotimi-best
19年6月19日在10:35

@ user210757等待,但是为什么会导致无限循环,这与您从服务器获取数据后不设置状态不同。如果要更新状态,则只需在调用useEffect中的函数之前检查条件是否为空,然后编写if条件。

– Rotimi-best
1月25日5:51

自从我评论以来,@ rotimi-best一直很不错,但是我会说每次都会重新创建该函数,因此永远不会一样,因此它将始终重新呈现,除非您进入useEffect主体或useCallback

–user210757
1月26日18:34