
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.
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;
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;
Here we used array destructuring. This is because useState
 returns two values:
post
 is the current state.updatePosts
 is the function that will update the value of the first variable, which is our current state.
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());
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 ( ... );
}
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;
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:
- The component renders and theÂ
useEffect
 is called on line:7 - It fetches the data and updates the state which causes a re-render
- The component renders again andÂ
useEffect
 is called once more. - The cycle continues.
To avoid infinite loops, we can pass a second optional parameter to useEffect
.
useEffect(() => {
getPost().then(posts => {
updatePosts(posts);
});
- });
+ }, []);
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());
})();
}, []);
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.

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>
);
}
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 useState
, useEffect
, 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!
Access exclusive interactive lessons
Unlimited access to hundreds of tutorials
Remove ads to learn without distractions