How To Easily Fetch Data With React Hooks

With the introduction to React Hooks from React v16.8, we can handle state in function components. This paves the way to leave behind classes and simplify our codebase, reduce bundle since, and overall, provide a better performance for users.

A common use-case of using hooks is fetching data from your server or from a third party API as soon as the component loads. We will take a look at how this can be done with the useState hook in conjunction with useEffect. Let’s dive in.

Looking to improve your skills? Check out our interactive course to master JavaScript from start to finish.
JavaScript Course

Setting Up React

To set things up, I’ve used create-react-app. If you already have it installed, run npx create-react-app react-hooks to bootstrap a new React application. Inside your App.js file, you can get rid of everything for now, but make sure you import useState and useEffect.

import React, { useState, useEffect } from 'react'
import './App.css'

function App() {
    return (
        <div className="App">
            <header className="App-header">
                <h1>👋</h1>
            </header>
        </div>
    );
}

export default App;
App.js
Copied to clipboard!

Using useState

Using useState lets you declare a state variable. A variable, that is bound to change. It provides the same functionality as this.state does in a class. Add the following line to your function component:

import React, { useState, useEffect } from 'react'
import './App.css'

function App() {
    const [post, updatePosts] = useState(null);

    return (
        <div className="App">
            <header className="App-header">
                <h1>👋</h1>
            </header>
        </div>
    );
}

export default App;
App.js
Copied to clipboard!

Here we used array destructuring. This is because useState returns two values:

You can name them however you like. The only important thing here is that you have your state as the first element, and a function which updates it as the second element.

And what is the passed argument for? It tells React the initial state. In our case, this will be null.

So we want to display a post. However, it is null, so we need to update the variable somehow.


Fetching the Post

To fetch some mock data, I’m going to be using JSON placeholder. Add the following function above your function component:

const getPost = () => fetch('https://jsonplaceholder.typicode.com/posts/1').then(response => response.json());
App.js
Copied to clipboard!

We can call this function to fetch a mock post. So you might think, all we need to do is call this function and call the update state function with the passed response.

function App() {
    const [post, updatePosts] = useState(null);

    getPost().then(response => updatePosts(response));
    
    return ( ... );
}
App.js
Copied to clipboard!

While this will certainly update the state, it will cause an infinite loop. This happens because as soon as the state is updated, the function component gets re-rendered. It will run into the fetch on line:4 again and set the state causing a new re-render. And so on. To prevent this, we need to use another hook, called useEffect.


Using useEffect

The useEffect hook can be used to create side effects in a component. Things such as managing subscriptions, changing the DOM manually, or fetching data. You can think useEffect as the componentDidMount or componentDidUpdate methods.

In our example, we will use it to only do a re-render once, when the data has arrived. Extend your component with the following:

import React, { useState, useEffect } from 'react'
import './App.css'

function App() {
    const [post, updatePosts] = useState(null);
    
    useEffect(() => {
        getPost().then(posts => {
            updatePosts(posts);
        });
    });
    
    return (
        <div className="App">
            <header className="App-header">
                <h1>👋</h1>
            </header>
        </div>
    );
}

export default App;
App.js
Copied to clipboard!

From line:7 till line:11, we use useEffect to handle the state change. It expects a callback function that will run every time the component renders. Inside it, we fetch the posts and update the variable with updatePosts.

The only problem with this is that it still causes an infinite loop. Let’s break down what will happen:

To avoid infinite loops, we can pass a second optional parameter to useEffect.

useEffect(() => {
     getPost().then(posts => {
         updatePosts(posts);
     });
- });
+ }, []);
App.diff
Copied to clipboard!

This tells useEffect to compare the values of the state between re-renders. If they don’t match — meaning the state has already been changed — useEffect won’t run. This breaks the cycle and makes our component work as expected.

Using Async/Await

If you’re dealing with multiple requests, making use of async/await also makes sense to improve readability. When doing so, keep the following in mind.

// ❌ This will throw an error
useEffect(async () => {
    updatePosts(await getPost());
}, []);

// ✔️ This won't
useEffect(() => {
    (async () => {
        updatePosts(await getPost());
    })();
}, []);
App.js
Copied to clipboard!

You need to place async functions inside the body of useEffect. This is because an effect must not return anything else besides a function. By making the function async, it will return a Promise.

Using async function in useEffect will cause error
You’ll also get a self-explanatory warning about returning an async function.

Putting together everything, the whole function will look like the following:

function App() {
    const [post, updatePosts] = useState(null);

    useEffect(() => {
        (async () => {
            updatePosts(await getPost());
        })();
    }, []);

    if (!post) {
        return <div>Loading...</div>
    }

    return (
        <div className="App">
            <header className="App-header">
                <h2>{post.title}</h2>
            </header>
            <main>{post.body}</main>
        </div>
    );
}
App.jsMake sure you check if the post is not null, to fall back to a loading screen
Copied to clipboard!

Wrapping Up

React Hooks are a powerful addition to React. Apart from useState and useEffect, React also provides you a way to create your own custom hooks. If you would like to learn more about useStateuseEffect, or hooks in general, make sure to check out their official documentation below:

Thank you for taking the time to read this article, happy coding!

JavaScript Course Dashboard

Tired of looking for tutorials?

You are not alone. Webtips has more than 400 tutorials which would take roughly 75 hours to read.

Check out our interactive course to master JavaScript in 5 hours.

Learn More

📚 Get access to exclusive content

Want to get access to exclusive content? Support webtips with the price of a coffee to get access to tips, checklists, cheatsheets, and much more. ☕

Get access Support us
Read more on
🎉 Thank you for subscribing to our newsletter. x