Fetching with useEffect
We've already learned about one hook, useState. Time for another one! In this lesson, we'll learn how to use the useEffect hook to send API fetch requests.
Table of Contents
Terms
Side effect — Anything that happens outside of React such sending a
fetchrequest, starting an animation, or setting up a server connection.Side effects can be triggered by user events like submitting a form or clicking a button.
useEffect– A react hook for executing "side effects" caused by a component rendering, not a particular event.Hooks — Functions that provide a wide variety of features for React components. They all begin with
use().
Dependency Array — The array of values provided to
useEffectthat React will watch for changes. If changes occur in the dependency array, the effect will run again.Conditional Rendering — Rendering different JSX depending on the current state. This can be useful when fetching to show either the fetched data or an error message if the fetch failed.
Fetching with event handlers
For a refresher on how to fetch, look at the
src/utils/fetchData.jshelper function. It returns an array with two values[data, error](a "tuple").
In React, sending a fetch request is referred to as an "effect" or "side effect" since it happens outside of the normal scope of what React handles.
Side effects are often executed in event handlers.
Check out the 1-joke-fetch-on-click React project. In our application, we can render utilize a random joke API to send a fetch request in response to a button click:
const JOKE_API_URL = "https://v2.jokeai.dev/joke/Pun?blacklistFlags=nsfw,religious,political,racist,sexist,explicit&type=twopart";
const defaultJoke = {
setup: "What do you call a pile of cats?",
delivery: "A meowntain",
};
function App() {
// Create state for the fetched data
const [joke, setJoke] = useState(defaultJoke);
// Always create state to store any errors
const [error, setError] = useState('');
// Make the event handler async
const handleClick = async () => {
const [data, error] = await fetchData(JOKE_API_URL);
if (data) setJoke(data);
if (error) setError(error);
}
// Conditional Rendering
if (error) return <p>{error.message}</p>
return (
<>
<button onClick={handleClick}>Get Random Joke</button>
<div className="joke">
<h1>{joke.setup}</h1>
<p>{joke.delivery}</p>
</div>
</>
);
}This example demonstrates a few important concepts:
When fetching, the fetched data should be stored in state (
joke)We should also make a piece of state to store an error if one is returned (
error)We can use conditional rendering to render an error message if there was one.
Challenge 1: Make a Dog API app
Let's create an app that fetches from the dog API and shows a random dog picture whenever the user clicks on a button.

The dog API https://dog.ceo/api/breeds/image/random returns an object like this:
{
"message": "https://images.dog.ceo/breeds/hound-walker/n02089867_1764.jpg",
"status": "success"
}Instructions:
Create the app
npm create vite@latest
# Name it dog-fetcher
# Select React
# Select JavaScript
cd dog-fetcher
npm i
npm run dev
# Delete the contents of App.jsxThen, copy the the
src/utilsfolder from the1-joke-fetch-on-clickfolder into your ownsrc/folder.Use the code in the
1-joke-fetch-on-click/srcfolder to guide you to creating this appThe
Appshould have adogPictureand anerrorstate. Visit the API https://dog.ceo/api/breeds/image/random to get an example object that you can use as the starting state fordogPicture. Something like this:
{
"message": "https://images.dog.ceo/breeds/hound-walker/n02089867_1764.jpg",
"status": "success"
}Replace the
Appcontents with your own app that has a<button>and an<img>. Theimgshould render thedogPicture.message.When the user clicks on the button, it should send a fetch to the dogAPI and update either the
dogPictureorerrorstate depending on the returned tupleAdd a conditional render to show the
error.messageif there is an error.
if (error) return <p>{error.message}</p>useEffect
There are two ways to perform a side effect like fetching:
In response to user events
In response to the component rendering ("reacting to the component rendering")
In our current joke API app, we only send a fetch in response to the user clicking on the button. But what if we want to show a joke when the page first renders?
We can accomplish this with the hook useEffect — a react hook for executing "side effects" caused by a component rendering, not a particular event.
Q: How do we know that this is a hook?
useEffect Syntax
useEffect takes in two arguments:
A callback function
[optional] A "dependency array"
It should be invoked at the top of the component, next to the other hooks used by the component (often below useState)
function App() {
const [joke, setJoke] = useState(defaultJoke);
const [error, setError] = useState();
// invoke useEffect at the top of the component, next to
// the other hooks
useEffect(() => {
const doFetch = async () => {
const [data, error] = await fetchData(JOKE_API_URL);
if (data) setJoke(data);
if (error) setError(error);
};
doFetch();
}, []);
// handleClick
// return JSX to render the joke
}Notice that this callback creates a async doFetch function that fetches, and sets the joke or the error state depending on what is returned.
The Effect callback
Why do we need to define doFetch and then invoke it? Why not just make the callback itself async.
Unfortunately, we can't make the callback async — we get an error
// Throws an error
useEffect(async () => {
const [data, error] = await fetchData(JOKE_API_URL);
if (data) setJoke(data);
if (error) setError(error);
}, []);So, inside of the callback, we make an async function that does the fetch and then invoke it immediately.
function App() {
const [joke, setJoke] = useState(defaultJoke);
const [error, setError] = useState();
useEffect(() => {
const doFetch = async () => {
const [data, error] = await fetchData(JOKE_API_URL);
if (data) setJoke(data);
if (error) setError(error);
};
doFetch();
}, []);
// handleClick
// return JSX to render the joke
}The Dependency Array
useEffect(effect, dependencyArray) needs to accept an effect callback but the second argument dependencyArray is optional. There are three ways that we can provide this value:
useEffect(effect); // execute after EVERY re-render
useEffect(effect, []); // only execute the effect once
useEffect(effect, [valueA, valueB]); // re-run the effect whenever the array changes between rendersIf the array is omitted, the effect is executed on EVERY render of the component.
If the array is empty, the effect is only executed on the first render of the component.
If the dependency array is provided, the effect will be only re-run on future renders if the values in the array change between renders.
Challenge 2: Fetch On Render
Add to your dog API app by having it render a dog image on the first render (and only on that first render!)
Fetching With a Form On Change
A cool way to fetch is using a form whenever the text input changes:
function App() {
const [joke, setJoke] = useState(defaultJoke);
const [error, setError] = useState();
const [query, setQuery] = useState("");
useEffect(() => {
const doFetch = async () => {
const [data, error] = await fetchData(`${JOKE_API_URL}&contains=${query}`);
if (data) setJoke(data);
if (error) setError(error);
};
doFetch();
}, [query]);
const handleClick = async () => {
const [data, error] = await fetchData(JOKE_API_URL);
if (data) setJoke(data);
if (error) setError(error);
}
if (error) return <p>{error.message}</p>
return (
<>
<form>
<input
type="text"
placeholder="query"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
</form>
<button onClick={handleClick}>Get Random Joke</button>
<div className="joke">
<h1>{joke.setup}</h1>
<p>{joke.delivery}</p>
</div>
</>
);
}Quiz
When should you
fetchusinguseEffectvs. an event handler?
Last updated