Javascript 对 useEffect 中异步函数的 React Hook 警告:useEffect 函数必须返回清理函数或不返回任何内容

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/53332321/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:05:58  来源:igfitidea点击:

React Hook Warnings for async function in useEffect: useEffect function must return a cleanup function or nothing

javascriptreactjsreact-hooks

提问by RedPandaz

I was trying the useEffect example something like below:

我正在尝试 useEffect 示例,如下所示:

useEffect(async () => {
    try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
        const json = await response.json();
        setPosts(json.data.children.map(it => it.data));
    } catch (e) {
        console.error(e);
    }
}, []);

and I get this warning in my console. But the cleanup is optional for async calls I think. I am not sure why I get this warning. Linking sandbox for examples. https://codesandbox.io/s/24rj871r0penter image description here

我在控制台中收到此警告。但是我认为清理对于异步调用是可选的。我不确定为什么会收到此警告。链接沙箱示例。https://codesandbox.io/s/24rj871r0p在此处输入图片说明

回答by RTW

I suggest to look at Dan Abramov (one of the React core maintainers) answer here:

我建议在此处查看Dan Abramov(React 核心维护者之一)的答案

I think you're making it more complicated than it needs to be.

我认为你让它变得比它需要的更复杂。

function Example() {
  const [data, dataSet] = useState<any>(null)

  useEffect(() => {
    async function fetchMyAPI() {
      let response = await fetch('api/data')
      response = await response.json()
      dataSet(response)
    }

    fetchMyAPI()
  }, [])

  return <div>{JSON.stringify(data)}</div>
}

Longer term we'll discourage this pattern because it encourages race conditions. Such as — anything could happen between your call starts and ends, and you could have gotten new props. Instead, we'll recommend Suspense for data fetching which will look more like

从长远来看,我们将不鼓励这种模式,因为它鼓励竞争条件。比如——在你的通话开始和结束之间可能会发生任何事情,你可能会得到新的道具。相反,我们会推荐 Suspense 来获取数据,它看起来更像

const response = MyAPIResource.read();

and no effects. But in the meantime you can move the async stuff to a separate function and call it.

并且没有效果。但与此同时,您可以将异步内容移至单独的函数并调用它。

You can read more about experimental suspense here.

您可以在此处阅读有关实验悬念的更多信息。



If you want to use functions outside with eslint.

如果你想在 eslint 之外使用函数。

 function OutsideUsageExample() {
  const [data, dataSet] = useState<any>(null)

  const fetchMyAPI = useCallback(async () => {
    let response = await fetch('api/data')
    response = await response.json()
    dataSet(response)
  }, [])

  useEffect(() => {
    fetchMyAPI()
  }, [fetchMyAPI])

  return (
    <div>
      <div>data: {JSON.stringify(data)}</div>
      <div>
        <button onClick={fetchMyAPI}>manual fetch</button>
      </div>
    </div>
  )
}


With useCallback useCallback. Sandbox.

使用 useCallback useCallback沙盒

import React, { useState, useEffect, useCallback } from "react";

export default function App() {
  const [counter, setCounter] = useState(1);

  // if counter is changed, than fn will be updated with new counter value
  const fn = useCallback(() => {
    setCounter(counter + 1);
  }, [counter]);

  // if counter is changed, than fn will not be updated and counter will be always 1 inside fn
  /*const fnBad = useCallback(() => {
      setCounter(counter + 1);
    }, []);*/

  // if fn or counter is changed, than useEffect will rerun
  useEffect(() => {
    if (!(counter % 2)) return; // this will stop the loop if counter is not even

    fn();
  }, [fn, counter]);

  // this will be infinite loop because fn is always changing with new counter value
  /*useEffect(() => {
    fn();
  }, [fn]);*/

  return (
    <div>
      <div>Counter is {counter}</div>
      <button onClick={fn}>add +1 count</button>
    </div>
  );
}

回答by Shubham Khatri

When you use an async function like

当您使用异步函数时

async () => {
    try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
        const json = await response.json();
        setPosts(json.data.children.map(it => it.data));
    } catch (e) {
        console.error(e);
    }
}

it returns a promise and useEffectdoesn't expect the callback function to return Promise, rather it expects that nothing is returned or a function is returned.

它返回一个useEffectPromise并且不期望回调函数返回 Promise,而是期望不返回任何内容或返回一个函数。

As a workaround for the warning you can use a self invoking async function.

作为警告的解决方法,您可以使用自调用异步函数。

useEffect(() => {
    (async function() {
        try {
            const response = await fetch(
                `https://www.reddit.com/r/${subreddit}.json`
            );
            const json = await response.json();
            setPosts(json.data.children.map(it => it.data));
        } catch (e) {
            console.error(e);
        }
    })();
}, []);

or to make it more cleaner you could define a function and then call it

或者为了让它更干净,你可以定义一个函数然后调用它

useEffect(() => {
    async function fetchData() {
        try {
            const response = await fetch(
                `https://www.reddit.com/r/${subreddit}.json`
            );
            const json = await response.json();
            setPosts(json.data.children.map(it => it.data));
        } catch (e) {
            console.error(e);
        }
    };
    fetchData();
}, []);

the second solution will make it easier to read and will help you write code to cancel previous requests if a new one is fired or save the latest request response in state

第二种解决方案将使其更易于阅读,并将帮助您编写代码以在触发新请求时取消先前的请求或将最新的请求响应保存在状态

Working codesandbox

工作代码沙盒

回答by Ed I

Until React provides a better way, you can create a helper, useEffectAsync.js:

在 React 提供更好的方法之前,您可以创建一个助手,useEffectAsync.js

import { useEffect } from 'react';


export default function useEffectAsync(effect, inputs) {
    useEffect(() => {
        effect();
    }, inputs);
}

Now you can pass an async function:

现在你可以传递一个异步函数:

useEffectAsync(async () => {
    const items = await fetchSomeItems();
    console.log(items);
}, []);

回答by Chiranjib

I read through this question, and feel the best way to implement useEffect is not mentioned in the answers. Let's say you have a network call, and would like to do something once you have the response. For the sake of simplicity, let's store the network response in a state variable. One might want to use action/reducer to update the store with the network response.

我通读了这个问题,感觉答案中没有提到实现 useEffect 的最佳方法。假设您有一个网络呼叫,并希望在收到响应后执行某些操作。为简单起见,让我们将网络响应存储在状态变量中。人们可能想要使用 action/reducer 来更新带有网络响应的存储。

const [data, setData] = useState(null);

/* This would be called on initial page load */
useEffect(()=>{
    fetch(`https://www.reddit.com/r/${subreddit}.json`)
    .then(data => {
        setData(data);
    })
    .catch(err => {
        /* perform error handling if desired */
    });
}, [])

/* This would be called when store/state data is updated */
useEffect(()=>{
    if (data) {
        setPosts(data.children.map(it => {
            /* do what you want */
        }));
    }
}, [data]);

Reference => https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

参考 => https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

回答by Nishla

try

尝试

const MyFunctionnalComponent: React.FC = props => {
  useEffect(() => {
    // Using an IIFE
    (async function anyNameFunction() {
      await loadContent();
    })();
  }, []);
  return <div></div>;
};

回答by Simon

For other readers, the error can come from the fact that there is no brackets wrapping the async function:

对于其他读者,错误可能来自于没有括号包裹 async 函数的事实:

Considering the async function initData

考虑异步函数 initData

  async function initData() {
  }

This code will lead to your error:

此代码将导致您的错误:

  useEffect(() => initData(), []);

But this one, won't:

但是这个,不会:

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

(Notice the brackets around initData()

(注意 initData() 周围的括号

回答by kajkal

void operatorcould be used here.
Instead of:

此处可以使用void 运算符
代替:

React.useEffect(() => {
    async function fetchData() {
    }
    fetchData();
}, []);

or

或者

React.useEffect(() => {
    (async function fetchData() {
    })()
}, []);

you could write:

你可以写:

React.useEffect(() => {
    void async function fetchData() {
    }();
}, []);

It is a little bit cleaner and prettier.

它更干净,更漂亮。