Fetching Data in React with useEffect

So you're building a component, and need to fetch some data from an API before rendering your component. You've been told that useEffect is the way for fetching data, but all you can find is discussion about side-effects (particularly in the official useEffect docs).

Let's unpack what side-effects are, and fetch ourselves some data.

What are side-effects?

To talk about side-effects, we should probably first talk about pure functions.

A pure function is a function, that when given an input, will always returns the same output. Pure functions also don't modify the arguments passed to them.

While you can have pure functions as simple as this:

function myPureFunction(count) {
return count + 1;
}

We can also have pure functions that return JSX in React:

function MyComponent(props) {
return <div className={props.className}>Some content goes here</div>;
}

Side-effects then, are operations that change things outside of your function, making the function impure.

Fetching data from an API, communicating with a database, and sending logs to a logging service are all considered side-effects, as it's possible to have a different output for the same input. For example, your request might fail, your database might be unreachable, or your logging service might have reached its quota.

This is why useEffect is the hook for us - by fetching data, we're making our React component impure, and useEffect provides us a safe place to write impure code.

Fetching data

Here's how to use useEffect to only fetch data (there are a few more steps to make it useful):

useEffect(() => {
const fetchData = async () => {
const response = await fetch(`https://swapi.dev/api/people/1/`);
const newData = await response.json();
};
fetchData();
});

You might notice a few things are missing from this example:

  • we're not doing anything with the data once we fetch it
  • we've hardcoded the URL to fetch data from

To make this useEffect useful, we'll need to:

  • update our useEffect to pass a prop called id to the URL,
  • use a dependency array, so that we only run this useEffect when id changes, and then
  • use the useState hook to store our data so we can display it later
useEffect(() => {
const fetchData = async () => {
const response = await fetch(`https://swapi.dev/api/people/${props.id}/`);
const newData = await response.json();
setData(newData);
};
fetchData();
}, [props.id]);

Now let's build the rest of the component. It needs to:

  • store our fetched data using useState
  • render part of the data when we do have data
  • render nothing if we don't have data
import React, { useState } from 'react';
export default function DataDisplayer(props) {
const [data, setData] = useState(null);
if (data) {
return <div>{data.name}</div>;
} else {
return null;
}
}

Finally, putting our pure React component together with our side-effect:

import React, { useEffect, useState } from 'react';
export default function DataDisplayer(props) {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch(`https://swapi.dev/api/people/${props.id}/`);
const newData = await response.json();
setData(newData);
};
fetchData();
}, [props.id]);
if (data) {
return <div>{data.name}</div>;
} else {
return null;
}
}

You might find fetching data in this way results in quite a bit of repeated code, especially if you're using fetch, and handle error and loading states. To avoid this, a common solution is to write a custom hook.

One caveat however: this code works fine if you want to fetch data once - but if you attach a button to let users change id too quickly, you'll run into a race condition!

You might also want to read this article about fixing race conditions with useEffect.

If you also want caching (to avoid requesting the same data twice), I highly recommend looking into one of these data fetching libraries instead.

(Shameless plug for the useEffect book I wrote below)

Tired of infinite re-renders when using useEffect?

A few years ago when I worked at Atlassian, a useEffect bug I wrote took down part of Jira for roughly one hour.

Knowing thousands of customers can't work because of a bug you wrote is a terrible feeling. To save others from making the same mistakes, I wrote a single resource that answers all of your questions about useEffect, after teaching it here on my blog for the last couple of years. It's packed with examples to get you confident writing and refactoring your useEffect code.

In a single afternoon, you'll learn how to fetch data with useEffect, how to use the dependency array, even how to prevent infinite re-renders with useCallback.

Master useEffect, in a single afternoon.

useEffect By Example's book cover