Note: If you want to support SSR/SSG for SEO, use framework specific api from React-router/Remix, Next.js or Gatsby.

For React version >=19

We now have nice loader and form action api:

Loading data:

function Comments({ dataPromise }) {
  const data = use(dataPromise);
  return <div>{JSON.stringify(data)}</div>;
}

async function loadData() {
  return { data: "some data" };
}

export function Index() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments dataPromise={loadData()} />
    </Suspense>
  );
}

Form actions

async function updateDataAndLoadNew() {
  // update data
  // load new data

  // return new data
  return { data: "some data" };
}

export default function Index() {
  const [state, action, isPending] = useActionState(
    async (prev, formData) => {
      return updateUserAndLoadNewData();
    },
    { data: "initial state, no data" } // can be null
  );

  if (state.error) {
    return `Error ${state.error.message}`;
  }

  return (
    <form action={action}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>
        Do it
      </button>
      {state.data && <p>Data {state.data}</p>}
    </form>
  );
}

For React version >=18

Starting with React 18 you can also use Suspense, but it's not yet recommended if you are not using frameworks that correctly implement it:

In React 18, you can start using Suspense for data fetching in opinionated frameworks like Relay, Next.js, Hydrogen, or Remix. Ad hoc data fetching with Suspense is technically possible, but still not recommended as a general strategy.

If not part of the framework, you can try some libs that implement it like swr.


Oversimplified example of how suspense works. You need to throw a promise for Suspense to catch it, show fallback component first and render Main component when promise it's resolved.

let fullfilled = false;
let promise;

const fetchData = () => {
  if (!fullfilled) {
    if (!promise) {
      promise = new Promise(async (resolve) => {
        const res = await fetch('api/data')
        const data = await res.json()

        fullfilled = true
        resolve(data)
      });
    }

    throw promise
  }
};

const Main = () => {
  fetchData();
  return <div>Loaded</div>;
};

const App = () => (
  <Suspense fallback={"Loading..."}>
    <Main />
  </Suspense>
);

For React version <=17

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>
  )
}
Answer from ZiiMakc on Stack Overflow
🌐
React
react.dev › reference › react › useEffect
useEffect – React
You can also rewrite using the async / await syntax, but you still need to provide a cleanup function: ... import { useState, useEffect } from 'react'; import { fetchBio } from './api.js'; export default function Page() { const [person, setPerson] = useState('Alice'); const [bio, setBio] = useState(null); useEffect(() => { async function startFetching() { setBio(null); const result = await fetchBio(person); if (!ignore) { setBio(result); } } let ignore = false; startFetching(); return () => { ignore = true; } }, [person]); return ( <> <select value={person} onChange={e => { setPerson(e.target.value); }}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> <option value="Taylor">Taylor</option> </select> <hr /> <p><i>{bio ??
Top answer
1 of 16
863

Note: If you want to support SSR/SSG for SEO, use framework specific api from React-router/Remix, Next.js or Gatsby.

For React version >=19

We now have nice loader and form action api:

Loading data:

function Comments({ dataPromise }) {
  const data = use(dataPromise);
  return <div>{JSON.stringify(data)}</div>;
}

async function loadData() {
  return { data: "some data" };
}

export function Index() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Comments dataPromise={loadData()} />
    </Suspense>
  );
}

Form actions

async function updateDataAndLoadNew() {
  // update data
  // load new data

  // return new data
  return { data: "some data" };
}

export default function Index() {
  const [state, action, isPending] = useActionState(
    async (prev, formData) => {
      return updateUserAndLoadNewData();
    },
    { data: "initial state, no data" } // can be null
  );

  if (state.error) {
    return `Error ${state.error.message}`;
  }

  return (
    <form action={action}>
      <input type="text" name="name" />
      <button type="submit" disabled={isPending}>
        Do it
      </button>
      {state.data && <p>Data {state.data}</p>}
    </form>
  );
}

For React version >=18

Starting with React 18 you can also use Suspense, but it's not yet recommended if you are not using frameworks that correctly implement it:

In React 18, you can start using Suspense for data fetching in opinionated frameworks like Relay, Next.js, Hydrogen, or Remix. Ad hoc data fetching with Suspense is technically possible, but still not recommended as a general strategy.

If not part of the framework, you can try some libs that implement it like swr.


Oversimplified example of how suspense works. You need to throw a promise for Suspense to catch it, show fallback component first and render Main component when promise it's resolved.

let fullfilled = false;
let promise;

const fetchData = () => {
  if (!fullfilled) {
    if (!promise) {
      promise = new Promise(async (resolve) => {
        const res = await fetch('api/data')
        const data = await res.json()

        fullfilled = true
        resolve(data)
      });
    }

    throw promise
  }
};

const Main = () => {
  fetchData();
  return <div>Loaded</div>;
};

const App = () => (
  <Suspense fallback={"Loading..."}>
    <Main />
  </Suspense>
);

For React version <=17

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>
  )
}
2 of 16
156

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 useEffect doesn't expect the callback function to return Promise, rather it expects that nothing is returned or a function is returned.

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

Discussions

React useEffect hook and Async/await own fetch data func?
I tried to create a function for fetching data from the server, and it works. But I am not sure if that the right way? I created a function component to fetching data, using useState, useEffect ... More on stackoverflow.com
🌐 stackoverflow.com
Setting React state from JSON data in useEffect
I need to fetch json data and iterate ... setting React states. I'd like to use a general function to handle all file requests. I'd also like to handle different types of errors (404 and json parsing, mostly). I've read How to use async functions in useEffect and several other ... More on stackoverflow.com
🌐 stackoverflow.com
useEffect vs async/await
if using async inside useEffect it usually looks like this useEffect(() => { const fetchData = async ()=>{ try{ setLoading(true) await getPost(id) setLoading(false) } catch (error) { setLoading(false) //handle error } } if(!id) return; fetchData() }, [id]); More on reddit.com
🌐 r/react
19
6
September 15, 2023
How to use async functions in useEffect (with examples)
Sure, it will work, just don't forget cleanup: useEffect(() => { let isCanceled = false; const fetchData = async () => { const data = await fetch(`https://yourapi.com?someParam=${param}`); const json = await response.json(); if (!isCanceled) { setData(json); } } fetchData(); return () => { isCanceled = true; }; }, [param]) Otherwise you may get race condition: say, dependency changes before response comes and useEffect triggers and sends second request. Once responses return in wrong order, you may get you UI rendering incorrect data. This point is not directly related to "how to use await inside of useEffect?" but I notice people missed this point really often when dealing with async operations in useEffect More on reddit.com
🌐 r/reactjs
36
47
August 14, 2021
🌐
Medium
medium.com › @finnkumar6 › battle-of-the-asyncs-promises-async-await-and-useeffect-in-reactjs-b1c83128d03e
Battle of the Asyncs: Promises, Async/Await, and useEffect in ReactJS | by Aryan kumar | Medium
October 13, 2024 - async function fetchData() { try { const response = await fetch('/api/data'); const data = await response.json(); console.log(data); } catch (error) { console.error("Error fetching data:", error); } } ... Asynchronous programming in JavaScript can feel like a battle — especially when working with React. With so many ways to handle async operations — Promises, async/await, and the quirks of useEffect — it’s easy to get lost in the chaos.
🌐
DEV Community
dev.to › jasmin › how-to-use-async-function-in-useeffect-5efc
How to use async function in useEffect? - DEV Community
June 20, 2022 - In this case we need to wrap our async function in useCallback to map it with dependency array. Note - If we do not wrap the function using useCallback hook it will re-render on every update which will result in triggering the useEffect hook again.
Top answer
1 of 2
10

Overall, you are heading in the right direction. For fetching data, you'd wanna use useEffect and pass [] as a second argument to make sure it fires only on initial mount.

I believe you could benefit from decoupling fetchJson function and making it more generic, as such:

const fetchJson = async (url) => {
  const response = await fetch(url);
  return response.json();
};

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

  useEffect(() => {
    fetchJson("https://api.coindesk.com/v1/bpi/currentprice.json")
      .then(({ disclaimer }) => setData(disclaimer));
  }, []);

  return <div>{data}</div>;
};
2 of 2
7

Another option is to use a self invoking function:

const Fetch = () => {
  const [data, setData] = useState(null);
  useEffect(() => {
    (async () => {
      let res = await fetch(
        "https://api.coindesk.com/v1/bpi/currentprice.json" //example and simple data
      );
      let response = await res.json();
      setData(response);
    })();
  }, []);
  return <div>{data}</div>;
};

The suggestion to separate out the fetch logic to a separate function is a good idea and can be done as follows:

const Fetch = () => {
  const [data, setData] = useState(null);
  useEffect(() => {
    (async () => {
      let response= await fetchData("https://api.coindesk.com/v1/bpi/currentprice.json");
      setData(response);
    })();
  }, []);
  return <div>{data}</div>;
};

const fetchData = async (url) => {
  const response = await fetch(url);
  const json = await response.json();

  return json;
};

And yet another option is to create a wrapper function around useEffect that triggers the async function for you similar to this:

export function useAsyncEffect(effect: () => Promise<any>) {
  useEffect(() => {
    effect().catch(e => console.warn("useAsyncEffect error", e));
  });
}
🌐
DEV Community
dev.to › stlnick › useeffect-and-async-4da8
`useEffect()` and `async` - DEV Community
August 10, 2020 - If you've learned the traditional class-based React Components and you're now trying to move into Hooks there's a few things along the way that'll throw you for a loop. One such thing that took a little digging for me is the combination of useEffect() - essentially the replacement for componentDidMount, componentDidUpdate, and componentWillUnmount - and async/await.
🌐
Ultimate Courses
ultimatecourses.com › blog › using-async-await-inside-react-use-effect-hook
Using Async Await Inside React's useEffect() Hook - Ultimate Courses
// ❌ Don't do this! useEffect(async () => { const users = await fetchUsers(); setUsers(users); return () => { // this never gets called, hello memory leaks... }; }, []); This WORKS, but you should avoid it. Why? Because React’s useEffect hook expects a cleanup function returned from it which is called when the component unmounts.
Find elsewhere
🌐
Devtrium
devtrium.com › posts › async-functions-useeffect
How to use async functions in useEffect (with examples) - Devtrium
August 14, 2021 - But an async function returns a Promise, which can't be called as a function! It's simply not what the useEffect hook expects for its first argument.
🌐
Reddit
reddit.com › r/react › useeffect vs async/await
r/react on Reddit: useEffect vs async/await
September 15, 2023 -

New to react.

So, I wrote some code and needed to refactor out to hooks. Couldn’t get the hook to work until I realized my

 const useMyHook = async () => {
      useEffect(()=>{
           calling API stuff
      }, [])
      return {blah, blah, blah}
 }

had async wrapped around useEffect not inside. Vexed me for a few days. Sat down this morning and realized that pattern was maybe not correct. Took out that async and it works.

Forgive my potential syntax above.

So I’m trying to make sure I understand useEffect with this.

useEffect is not a replacement for async, but seems to resolve just fine without any async/await internals for an API call. Why is this and will I run into issues with longer wait times for responses?

Is best practice to use async functions inside of useEffect and then call those functions within the same useEffect with await then return? If not, what is the best practice?

I probably also need more info about useEffect. I find the docs kinda split between too low level technical and too high level.

My understanding so far is that it is a built in hook intended to be used when accessing external services. But I don’t really know why besides the convenience of monitoring some state and firing when it changes. Especially not sure why if you still have to implement async functions inside of it. Not sure what exactly useEffect provides.

🌐
Docureacten
docureacten.github.io › managing asynchronous operations inside useeffect
Managing Asynchronous Operations Inside useEffect | React.js: Learn Easily with Examples
Avoid making useEffect asynchronous directly: Instead, create an inner async function within the useEffect. Handle cleanup properly: Use a flag like isMounted to ensure state updates only occur if the component is still mounted.
🌐
React
react.dev › learn › synchronizing-with-effects
Synchronizing with Effects – React
useEffect(() => { let ignore = false; async function startFetching() { const json = await fetchTodos(userId); if (!ignore) { setTodos(json); } } startFetching(); return () => { ignore = true; }; }, [userId]); You can’t “undo” a network request that already happened, but your cleanup function should ensure that the fetch that’s not relevant anymore does not keep affecting your application.
🌐
Max Rozen
maxrozen.com › fetching-data-react-with-useeffect
Fetching Data in React with useEffect - Max Rozen
useEffect(() => { const fetchData = async () => { // highlight-next-line const response = await fetch(`https://swapi.dev/api/people/${props.id}/`); const newData = await response.json(); // highlight-next-line setData(newData); }; fetchData(); // highlight-next-line }, [props.id]); Now let's build the rest of the component. It needs to: ... import React, { useState } from 'react'; export default function DataDisplayer(props) { // highlight-next-line const [data, setData] = useState(null); // highlight-next-line if (data) { return <div>{data.name}</div>; // highlight-next-line } else { return null; } }
🌐
Reddit
reddit.com › r/reactjs › how to use async functions in useeffect (with examples)
r/reactjs on Reddit: How to use async functions in useEffect (with examples)
August 14, 2021 - Join the Reactiflux Discord (reactiflux.com) for additional React discussion and help. ... useEffect(() => { let isCanceled = false; const fetchData = async () => { const data = await fetch(`https://yourapi.com?someParam=${param}`); const json = await response.json(); if (!isCanceled) { setData(json); } } fetchData(); return () => { isCanceled = true; }; }, [param])
Top answer
1 of 2
2

There's a couple of problems with this snippet:

{videos.id.map((video) => {
  <div key={video.id} {...videos}></div>
})}
  1. In your code the videos variable is initially null, and then (after fetching) — an array. Arrays don't have id property, and accessing properties of null is an illegal operation. You might want to first ensure that this is an array, rather than null, and then also remove the ".id" part.

  2. You don't return anything from .map()! Yes, you create a JSX element for each item of the array, but they are destroyed unused after that.

Consider using this instead:

{videos && videos.map((video) => (
  <div key={video.id} {...videos}></div>
))}

Another problem is with variables' visibility:

import videos from "./path/to/data.json";
// and then later
const [ videos, setVideos ] = useState(null);

That first variable videos (the imported one) is not visible anymore, it got shadowed by the second one.

You either rename the second variable to prevent shadowing, or remove the first one completely as it is unused.


Next thing that I can see is that the code can't make up its mind about what it is actually trying to accomplish. On one hand, router provides an ID to a specific, particular video, which means that we're trying to show only one video. On the other hand though, the FeaturedVideo component is almost a perfect fit for showing the list of all the videos.

Judging from the names and overall setup though, it is somewhat clear that you're trying to show one video, not the whole list.

Looks like you're using react-router. If that's true, in FeaturedVideo you need to access the video ID, provided by router. Given that it is v5+, you can use useParams hook for that:

import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router';
const [ video, setVideo ] = useState(null); // 'video', not 'videos'
const { videoID } = useParams();

useEffect(() => {
  fetch("/src/data/data.json")
    .then((res) => res.json())
    .then((videos) => videos.filter((video) => {
      return video.id === videoID;
    }))
    .then((matched) => setVideo(matched[0]));
}, []);

// Here `video` variable holds either `null` or the first matched array item.
// Render it using suggestions above.

References:

  • .map() method of arrays
  • && operator (see Description section)
  • Variable shadowing
  • React Router Hooks
2 of 2
1

You are correct in using that useEffect hook. You want this data to load on component mount.

Assuming that URL returns JSON you can simply:

fetch(url)
  .then(res => res.json().then(videos => setVideos(videos)));
🌐
Medium
medium.com › @almustarik › why-you-cant-use-async-functions-directly-in-useeffect-and-how-to-handle-asynchronous-side-effects-fbf529242e49
Why You Can’t Use async Functions Directly in useEffect and How to Handle Asynchronous Side Effects in React | by Al Mustarik | Medium
May 22, 2024 - Why useEffect Can't Accept async Functions Directly · React’s useEffect does not accept async functions because an async function always returns a Promise, which is not compatible with the expected return type of void or a cleanup function.
🌐
Medium
arkumari2000.medium.com › fetch-data-from-a-local-json-file-in-react-js-in-functional-component-be1a4b66ebd1
Fetch Data from a local JSON file in react js (in functional component) | by Archana Kumari | Medium
May 31, 2021 - utils/get-data.js export const getData = async (filePath, fileType) => { try{ const response = await fetch(filePath); switch (fileType.toUpperCase()) { case 'JSON': return response.json(); default: return response; } } catch (error) { return error; } } We can give filePath and fileType a value like this: filePath="path of json file", fileType="file type here" Custom Hook file: hooks/customHook.js import { useState, useEffect } from 'react'; import { getData } from '../utils';export function customHook(){ const [data, setData] = useState(); useEffect(()=>{ fetchData(); },[]) const fetchData = a
🌐
Medium
medium.com › @atshn.gunduz › fetch-async-useeffect-a5737846bf9c
fetch async useEffect. Fetching data asynchronously allows our… | by Ateshan Gunduz | Medium
December 8, 2023 - export async function fetchCat() { try { const res = await fetch( "https://api.thecatapi.com/v1/images/search?limit=8&size=full&breed_id=beng&sub_id=demo-ca06d4" ); const cat = await res.json(); return cat; } catch (e) {} } Moreover, in React we need useEffect hook to handle our asynchronous fetch request.
🌐
Educative
educative.io › answers › how-to-handle-asynchronous-operations-in-react
How to handle asynchronous operations in React
Lines 7–9: This sets up an effect using the useEffect hook. It runs the fetchData function when the component mounts ([] dependency array indicates that the effect · Lines 11–13: We define an asynchronous function named fetchData using the async keyword that uses the Axios library to send a GET request to the specified URL and waits for the response using the await keyword.
🌐
Samuel Lawrentz
samuellawrentz.com › hacks › react › handling-asynchronous-operations-in-useeffect
Mastering Asynchronous Operations in useEffect
import React, { useEffect, useState } from 'react'; function Example() { const [data, setData] = useState([]); useEffect(() => { const fetchData = async () => { const response = await fetch('https://api.example.com'); const data = await response.json(); setData(data); }; fetchData(); }, []); return ( // Render your component ); }