How to call an async function inside a UseEffect() in React?

JavascriptReactjsAsynchronousReact Hooks

Javascript Problem Overview


I would like to call an async function and get the result for my UseEffect.

The fetch api examples i found on the internet are directly made in the useEffect function. If my URL changes, i must patch all my fetchs.

When i tried, i got an error message.

This is my code.


    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 Warning: An effect function must not return anything besides a function, which is used for clean-up. It looks like you wrote useEffect(async () => ...) or returned a Promise. Instead, write the async function inside your effect and call it immediately:

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

Javascript Solutions


Solution 1 - Javascript

Create an async function inside your effect that wait the getData(1) result then call setData():

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

Solution 2 - Javascript

If you're invoking it right-away you might want to use it as an anonymous function:

useEffect(() => {

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

}, []);

Solution 3 - Javascript

It would be best if you did what the warning suggests - call the async function inside the effect.

    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>
        );
    }

If you want to keep the api function outside of the component, you can also do this:

    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 () => {
                const newData = await getData(1);
                setData(newData);
            })();
        }, []);

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

Solution 4 - Javascript

Since getData returns a Promise you could just use .then

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

Solution 5 - Javascript

You can still define the async function outside of the hook and call it within the hook.

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

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

Solution 6 - Javascript

Component might unmount or re-render with different someId before await is resolved:

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]);

Consider using Suspense for data that needs to be loaded before component is mounted.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionYassine LayachiView Question on Stackoverflow
Solution 1 - JavascriptFractionView Answer on Stackoverflow
Solution 2 - JavascripthsusanooView Answer on Stackoverflow
Solution 3 - JavascriptzilijonasView Answer on Stackoverflow
Solution 4 - JavascriptYingYangView Answer on Stackoverflow
Solution 5 - JavascriptJamesKView Answer on Stackoverflow
Solution 6 - JavascriptbrunettdanView Answer on Stackoverflow