Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I was trying the useEffect example something like below:

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/24rj871r0p enter image description here

like image 579
RedPandaz Avatar asked Nov 16 '18 06:11

RedPandaz


People also ask

Can you use async with useEffect?

Either way, we're now safe to use async functions inside useEffect hooks. Now if/when you want to return a cleanup function, it will get called and we also keep useEffect nice and clean and free from race conditions. Enjoy using async functions with React's useEffect from here on out!

Why does the callback function in the useEffect hook Cannot be asynchronous?

You cannot directly make the callback function supplied to the useEffect hook async because: async functions implicitly return a promise, and; useEffect expects its callback to either return nothing or a clean-up function.

What does useEffect hook return?

Anatomy of the useEffect hook The return function is the cleanup function, or when the user leaves the page and the component will unmount. The array is the last part, and it is where you put the states that will update throughout the component's lifecycle.

How does useEffect () hook gets executed?

useEffect() hook runs the side-effect after initial rendering, and on later renderings only if the name value changes.


1 Answers

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

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

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.

 function OutsideUsageExample({ userId }) {   const [data, dataSet] = useState<any>(null)    const fetchMyAPI = useCallback(async () => {     let response = await fetch('api/data/' + userId)     response = await response.json()     dataSet(response)   }, [userId]) // if userId changes, useEffect will run again    useEffect(() => {     fetchMyAPI()   }, [fetchMyAPI])    return (     <div>       <div>data: {JSON.stringify(data)}</div>       <div>         <button onClick={fetchMyAPI}>manual fetch</button>       </div>     </div>   ) } 

If you use useCallback, look at example of how it works useCallback. Sandbox.

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>   ); } 
like image 116
ZiiMakc Avatar answered Oct 09 '22 09:10

ZiiMakc