如何在 React 的 UseEffect() 中调用异步函数?

IT技术 javascript reactjs asynchronous react-hooks
2021-01-30 02:44:50

我想调用一个异步函数并获取我的 UseEffect 的结果。

我在网上找到的fetch api例子是直接在useEffect函数中制作的。如果我的 URL 发生变化,我必须修补我所有的提取。

当我尝试时,我收到一条错误消息。

这是我的代码。


    async function getData(userId) {
        const data = await axios.get(`http://url/api/data/${userId}`)
            .then(promise => {
                return promise.data;
            })
            .catch(e => {
                console.error(e);
            })
            return data;
    }


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

        useEffect(async () => {
            setData(getData(1))
        }, []);

        return (
            <div>
                this is the {data["name"]}
            </div>
        );
    }

index.js:1375 警告:除了用于清理的函数之外,效果函数不得返回任何内容。看起来你写了 useEffect(async () => ...) 或者返回了一个 Promise。相反,在您的效果中编写异步函数并立即调用它:

useEffect(() => {
  async function fetchData() {
    // You can await here
    const response = await MyAPI.getData(someId);
    // ...
  }
  fetchData();
}, [someId]); // Or [] if effect doesn't need props or state
6个回答

在您的效果中创建一个异步函数,等待getData(1)结果然后调用setData()

useEffect(() => {
  const fetchData = async () => {
     const data = await getData(1);
     setData(data);
  }

  fetchData();
}, []);
为什么这与在 useEffect 钩子之外定义异步函数有什么不同?
2021-03-19 02:44:50
@LelandReardon如果要定义的异步函数外useEffect钩,你必须将它添加到的依赖项列表useEffect,敷其定义成useCallback与必要的依赖,以避免不必要的电话,以获得更多信息检查反应文档在这里
2021-03-25 02:44:50

如果您立即调用它,您可能希望将其用作匿名函数:

useEffect(() => {

  (async () => {
     const data = await getData(1);
     setData(data);
  })();

}, []);
如果组件被卸载,如何取消请求?
2021-03-21 02:44:50
@Remi 这与 OP 的问题无关,您可以在单独的线程上提出这个问题,因为它可能有不同的实现,其中大部分与您是使用匿名异步还是使用预定义异步来获取数据无关
2021-03-24 02:44:50
边缘情况。如果你没有取消它,那么即使这个组件被卸载,React 也会设置一个状态。想想一些测试库甚至会抱怨。因此这个例子不推荐 persé。
2021-03-27 02:44:50
有什么好处?
2021-04-08 02:44:50
@Remi 该函数将立即被调用,不会在其他任何地方使用,因此它不需要名称或任何预定义
2021-04-12 02:44:50

最好是按照警告的建议去做——在效果中调用 async 函数。

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

        useEffect(() => {
            axios.get(`http://url/api/data/1`)
             .then(result => {
                setData(result.data);
             })
             .catch(console.error)
        }, []);

        return (
            <div>
                this is the {data["name"]}
            </div>
        );
    }

如果你想将 api 函数保留在组件之外,你也可以这样做:

    async function getData(userId) {
        const data = await axios.get(`http://url/api/data/${userId}`)
            .then(promise => {
                return promise.data;
            })
            .catch(e => {
                console.error(e);
            })
            return data;
    }


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

        useEffect(() => {
            const updateData = async () => {
                const newData = await getData(1);
                setData(newData);
            };
            
            updateData();
        }, []);

        return (
            <div>
                this is the {data["name"]}
            </div>
        );
    }
谢谢你的回答。不幸的是,这正是我不想做的。如果我的 URL 发生变化,我必须在我获取的所有文件上修补它。对于可扩展性,我正在尝试做一些更像我发布的第一个代码的事情。
2021-03-25 02:44:50

由于getData返回一个 Promise,您可以使用.then

useEffect(() => {
    getData(1).then(setData);
}, []);

someId在解决 await 之前,组件可能会卸载或重新渲染为不同的

const unmountedRef = useRef(false);
useEffect(()=>()=>(unmountedRef.current = true), []);

useEffect(() => {
  const effectStale = false; // Don't forget ; on the line before self-invoking functions
  (async function() {
    // You can await here
    const response = await MyAPI.getData(someId);

    /* Component has been unmounted. Stop to avoid
       "Warning: Can't perform a React state update on an unmounted component." */
    if(unmountedRef.current) return;

    /* Component has re-rendered with different someId value
       Stop to avoid updating state with stale response */
    if(effectStale) return;

    // ... update component state
  })();
  return ()=>(effectStale = true);
}, [someId]);

对于需要在挂载组件之前加载的数据,请考虑使用Suspense