Debugging Microservices & Distributed Systems
4 min read

How To Fetch Data From an API With React Hooks

Fetch data in React applications using the power of React Hooks

React Hooks let us write pure functional components without ever using the class syntax. Usually, but not always, the less code we have to write, the faster we can build our application.

If you’re new to Hooks, check out this tutorial for an introduction to Hooks.

Step 1. Refactoring From a Class to a Functional Component

Take this React class-based component. Our first step to using Hooks would be to refactor it into a functional component. Remember, you can use Hooks only with functional components.

import React, { Component } from "react";
export default class Planets extends Component {
state = {
hasErrors: false,
planets: {}
};
componentDidMount() {
fetch("https://swapi.co/api/planets/4/")
.then(res => res.json())
.then(res => this.setState({ planets: res }))
.catch(() => this.setState({ hasErrors: true }));
}
render() {
return <div>{JSON.stringify(this.state.planets)}</div>;
}
}

The component has hasError and planets state variables. When the component mounts, we call the Star Wars API and fetch the information.

Here’s how we would transform the component into a functional component.

import React, { useState } from "react";
const Planets = () => {
const [hasError, setErrors] = useState(false)
const [planets, setPlanets] = useState({})
componentDidMount() {
fetch("https://swapi.co/api/planets/4/")
.then(res => res.json())
.then(res => this.setState({ planets: res }))
.catch(() => this.setState({ hasErrors: true }));
}
return (
<div>{JSON.stringify(planets)}</div>
)
}
export default Planets;

Tada! A functional component with Hooks. There’s just one problem, though: Hooks don’t have lifecycle methods such as componentDidMount or componentWillMount.

Step 2. useEffect

The useEffect Hook lets you perform side effects in function components. Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects.

import React, { useState, useEffect } from "react";
const Planets = () => {
const [hasError, setErrors] = useState(false);
const [planets, setPlanets] = useState({});
useEffect(() =>
fetch("https://swapi.co/api/planets/4/")
.then(res => res.json())
.then(res => this.setState({ planets: res }))
.catch(() => this.setState({ hasErrors: true }))
);
return <div>{JSON.stringify(planets)}</div>;
};
export default Planets;

useEffect is a React Hook that accepts a callback as its first argument. Inside the first argument, we perform all the effect-related tasks.

Import the useEffect from React, and replace componentWillMount with this Hook.

What does useEffect do?

By using this Hook, you tell React that your component needs to do something after rendering. React will remember the function you passed (we’ll refer to it as our “effect”) and call it later after performing the DOM updates.

But why is the state still our initial state {}? Because, there’s no this.setState({}) function inside functional components.

Step 3. Moving From this.setState({}) to Hooks
import React, { useState, useEffect } from "react";
const Planets = () => {
const [hasError, setErrors] = useState(false);
const [planets, setPlanets] = useState({});
useEffect(() => {
async function fetchData() {
const res = await fetch("https://swapi.co/api/planets/4/");
res
.json()
.then(res => setPlanets(res))
.catch(err => setErrors(err));
}
fetchData();
}, []);
return (
<div>
<span>{JSON.stringify(planets)}</span>
<hr />
<span>Has error: {JSON.stringify(hasError)}</span>
</div>
);
}
export default Planets;

Notice how we put a function inside the callback. The function is named fetchData and does exactly as it’s named.

If this looks more complicated, I assure you it’s not. Just like how we wrote a function inside componentDidMount, we do the same in the useEffect Hook.

To illustrate how simple it actually is, let’s move the fetchData function out of the Hook entirely.

import React, { useState, useEffect } from "react";
const Planets = () => {
const [hasError, setErrors] = useState(false);
const [planets, setPlanets] = useState({});
async function fetchData() {
const res = await fetch("https://swapi.co/api/planets/4/");
res
.json()
.then(res => setPlanets(res))
.catch(err => setErrors(err));
}
useEffect(() => {
fetchData();
}, []);
return (
<div>
<span>{JSON.stringify(planets)}</span>
<hr />
<span>Has error: {JSON.stringify(hasError)}</span>
</div>
);
};
export default Planets;

Note: If you want the useEffect to behave like the componentDidMount lifecycle event, pass an empty array as the second argument, like so:

useEffect(() => {
fetchData();
}, []);

Think of the second argument as the dependencies for that effect. If one of the dependencies has changed since the last time, the effect will run again.


Related Articles

If you enjoyed this article, you might find these related pieces interesting as well.

Recommended Engineering Resources

Here are engineering resources I've personally vetted and use. They focus on skills you'll actually need to build and scale real projects - the kind of experience that gets you hired or promoted.

Imagine where you would be in two years if you actually took the time to learn every day. A little effort consistently adds up, shaping your skills, opening doors, and building the career you envision. Start now, and future you will thank you.


This article was originally published on https://www.trevorlasn.com/blog/how-to-fetch-data-from-an-api-with-react-hooks. It was written by a human and polished using grammar tools for clarity.

Interested in a partnership? Shoot me an email at hi [at] trevorlasn.com with all relevant information.