Armaan Midha

How to have an async API call in useEffect Hook?

Cover Image for How to have an async API call in useEffect Hook?

By reading the title it seems like an easy thing to do. Right?

But trust me I have seen experienced developers make this silly mistake, it all happens because everything in the title seems familiar.

  • 1 UseEffect ✅
  • 2 async function ✅
  • 3 await API call ✅

I will take an example and code step by step.

Suppose we have a daily shopping list and we get the items in this list by an API on mount.

Let's take care of UI part first, I am hard coding the data for time being.

import React from "react"; export default function ShoppingList() { const data = [ { item: "Cheese", quantity: "3 Packs", price: "INR 300", }, { item: "Milk", quantity: "2 Litres", price: "INR 50", }, ]; return ( <table> <thead> <tr> <th>Item</th> <th>Quantity</th> <th>Price</th> </tr> </thead> <tbody> {data.map((item, index) => ( <tr key={index}> <td>{item.item}</td> <td>{item.quantity}</td> <td>{item.price}</td> </tr> ))} </tbody> </table> ); }

Output:-

Item NameQuantityPrice
Cheese3 PacksINR 300
Milk2 LitresINR 50

Pretty neat. Isn't it?

Coming to the API call.

import React from 'react'; import baseUrl from './constants' export default function ShoppingList() { const [data, setData] = React.useState([]); React.useEffect(() => { const url = `${baseUrl}/get_items`; const res = await fetch(url); const json = await res.json(); setData(json.data); }, []); if (!data) { return 'Loading…'; } return ( <table> <thead> <tr> <th>Item</th> <th>Quantity</th> <th>Price</th> </tr> </thead> <tbody> {data.map((item, index) => ( <tr key={index}> <td>{item.item}</td> <td>{item.quantity}</td> <td>{item.price}</td> </tr> ))} </tbody> </table> ); }

There's an error in the code.

'await' is only allowed within async functions

We can fix this.

React.useEffect(async () => { const url = `${baseUrl}/get_items`; const res = await fetch(url); const json = await res.json(); setData(json.data); }, []);

Now console shouldn't shout at us. But now it gives another error message.

'destroy' is not a function

What does it mean?
Let's understand what we did wrong first. We tried to return a promise through useEffect hook. But it isn't expected to return a promise. UseEffect should either return nothing or a cleanup function.

Cleanup functions run before the component unmounts to tidy up the code. This prevents memory leaks and removes some unwanted behaviors. So useEffect cleans up after itself by calling the cleanup function.

We know what we did wrong, now let's fix it.

React.useEffect(() => { // Create an async function to call the API inside useEffect async function getData() { const url = `${baseUrl}/get_items`; const res = await fetch(url); const json = await res.json(); setData(json.data); } // And then invoke it. getData(); }, []);

Now we all are happy 🍻 (including console).

Our getData function will make the api call on mount and display the data on the screen.

React is easy to learn but it takes a lot of time for developers to understand how it actually works. This is one of the moments where React tests your core knowledge and shows you it's wrath ☠️

We need to understand why react works the way it does. I'm sure if you do, it is the coolest library out there to learn.