我可以在 useEffect 中调用单独的函数吗?

Was*_*aya 16 reactjs react-hooks

我可以在 useEffect 中调用另一个单独的函数吗?

我正在 iuseEffect 中调用另一个函数,但在保存文件后,它会自动将该函数添加到 useEffect 的数组参数中。

请参阅下面的代码以正确理解。

保存文件前:

  useEffect(() => {
    getData()
    console.log("useEffect ran...");
  }, [query]);

  function getData() {
    fetch(`https://jsonplaceholder.typicode.com/${query}`)
    .then(response => response.json())
    .then(json => setData(json));
  }
Run Code Online (Sandbox Code Playgroud)

保存文件后:

  useEffect(() => {
    getData();
    console.log("useEffect ran...");
  }, [getData, query]);

  function getData() {
    fetch(`https://jsonplaceholder.typicode.com/${query}`)
      .then(response => response.json())
      .then(json => setData(json));
  }
Run Code Online (Sandbox Code Playgroud)

它一次又一次地运行。

Dan*_*elK 23

TL; DR 使用 useCallback()


首先,让一个函数成为依赖是非常危险的。如果该函数导致状态更改,则随后的重新渲染将再次调用该函数(通过 useEffect)...并开始无限循环。

正如许多人在这里建议的那样,您可以做的一件事是在 useEffect() 方法本身内创建函数。

 useEffect(() => {
    function getData() {
      fetch(`https://jsonplaceholder.typicode.com/${query}`)
        .then(response => response.json())
        .then(json => setData(json));
      }
    }

    getData();
    console.log("useEffect ran...");
  }, [query]);
}
Run Code Online (Sandbox Code Playgroud)

或者干脆

 useEffect(() => {
    (() => {
       fetch(`https://jsonplaceholder.typicode.com/${query}`)
      .then(response => response.json())
      .then(json => setData(json)
     )();
  }, [query]);
}
Run Code Online (Sandbox Code Playgroud)

话虽如此,有时您仍然希望在 useEffect 之外为code-reuse声明函数。这次您需要确保它不会在每次渲染期间重新创建。为此,您可以

  1. 在组件外声明函数 - 这迫使您将所有变量作为参数传递......这是一个痛苦

  2. 把它包在里面useMemo()useCallback()

这是一个例子

    const getData = useCallback(()=>{...}, []);
Run Code Online (Sandbox Code Playgroud)

  • 嘿!您的回答帮助我第一次理解了 useCallback!我可以用你的答案作为例子来解决我的问题,非常感谢! (7认同)

sve*_*hui 17

由于您getData在 React 组件中声明了该函数,它将在每次渲染时重新创建,因此效果的依赖关系在每次渲染时都会发生变化。这就是为什么在每次渲染上执行效果的原因。

为了防止这种情况,您应该getData在组件外部声明函数,然后传递查询。像这样:

function getData(query) {
  return fetch(`https://jsonplaceholder.typicode.com/${query}`)
    .then(response => response.json());
}

function YouComponent({ query }) {
  ...
  useEffect(() => {
    getData(query).then(setData);
    console.log("useEffect ran...");
  }, [query]);

  ...
Run Code Online (Sandbox Code Playgroud)

PS:我不确定 eslint 插件在这样做时是否会自动将 getData 添加到依赖项中,但即使这样做也不会造成伤害。


Bho*_*yar 8

接受的答案在某种程度上具有误导性。显然,在组件内部定义函数会在每次渲染时重新创建。但这并不意味着在使用 useEffect hook 内部时它会在每次渲染中重新调用。

保存文件代码后的关键问题是您正在监视 getData。由于它在每个渲染上重新创建,因此 useEffect 接受它作为更改,从而导致您在每个渲染上重新运行。简单的修复方法是不观看 getData。

但显然,正如已接受的答案中所建议的那样。更好的做法是将函数分离到组件之外,这样它就不会在每次渲染时重新创建。

坦率地说,如果我是你,我不会只为 fetch 定义函数:

useEffect(() => {
  fetch(`https://jsonplaceholder.typicode.com/${query}`)
    .then(response => response.json())
    .then(json => setData(json));
}, [query]); // only re-run on query change
Run Code Online (Sandbox Code Playgroud)

  • 最初的问题明确指出,在保存文件时会自动添加依赖项,我认为 eslint-plugin-react-hooks 是导致此问题的原因。在我看来,这是一种在 useEffect 中通过设计省略依赖项的反模式,因为一旦这些依赖项开始向外部闭包添加依赖项,当出现问题时,它可能会导致很多麻烦。 (2认同)