LinkPe

How to fetch data in React (with error handling)

Shivam Kumar

Shivam Kumar

May 2020

How to Fetch data in React, with error handling

Sometimes people get entangled in the component lifecycle of React to get data from API, I mean which tool/library would be best to use and what is the right method in React component’s lifecycle to fetch it correctly.

There are many popular libraries and built-in API for fetching data in the React component. In this tutorial, I will describe some of the best that I have found on the web.

In this tutorial, I'm using Github's users API to pull data.

How to fetch data in React using native window.fetch API


import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
// Initialize state first
this.state = {
    users: [],
    err: null,
    isLoading: false
 }
}
}

Then initialize the React lifecycle method componentDidMount to call fetch request.


componentDidMount() {
    this.setState({ isLoading: true })
    let api_url = 'https://api.github.com/users';
    // Now, use JavaScript's native Fetch API to get
    // users list from Github API
    fetch(api_url)
    .then(res => {
        // Unfortunately, fetch doesn't send (404 error) into the cache itself
        // You have to send it, as I have done below
        if(res.status >= 400) {
            throw new Error("Server responds with error!");
        }
        return res.json();
    })
    .then(users => {
        this.setState({
            users,
            isLoading: false
        })
    },
    // Note: it's important to handle errors here 
    // instead of a catch() block so that we don't swallow
    // exceptions from actual bugs in components
    err => {
        this.setState({
            err,
            isLoading: false
        })
    });
}

Now, this.state.users has users data array, render it into DOM.


render() {
    // Extract states in the local variable
    let {users, err, isLoading} = this.state;
    if(err) {
        // Here you can either render error message or Error component
        // In this example, I have used message
        return (
        <div> { err.message } </div>
        )
    }
    if(isLoading) {
        return (
        <div> Loading... </div>
        )
    }
    return (
    <div>
        {/* Here you can check whether the users array contains data or not. */}
        { users.length > 0 ?
            <ul>
                {users.map( user => (
                    <li key={user.id} >
                        { user.login }
                    </li>
                ))}
            </ul>
            : <div> No user found! </div> }
    </div>
    )
}

If you want to use the native fetch API in your App, I recommend using cross-fetch as a polyfill because some old browser/JS engine doesn't fully support fetch.

How to fetch data in React using Axios HTTP client

Axios is another popular option which you can use to make HTTP requests in React Component. Axios is Promise-based and works for both client (Browser) and server (Node) so you don't need to worry about, whether you hit a request from the server or in your browser.

Axios is a third-party tool and you have to install it first from npm or yarn to use.


import React, { Component } from 'react';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
// Initialize state first
this.state = {
    users: [],
    err: null,
    isLoading: false
 }
}
}

Then initialize componentDidMount lifecycle method of React to trigger axios request


componentDidMount() {
    this.setState({ isLoading: true })
    let api_url = 'https://api.github.com/users';
    // Now, To pull data from API use axios get method
    axios.get(api_url)
    // axios automatically changes the response to JSON
    .then(users => {
        this.setState({
        users: users.data,
        isLoading: false
        })
    })
    .catch(err => {
        this.setState({
        err,
        isLoading: false
        })
    });
}

You can create a new instance of axios with a custom config as I created below.


const request = axios.create({
    baseURL: 'https://api.github.com/',
    timeout: 1000,
    headers: {
        'Content-Type': 'application/json',
        // or whatever you want, in key/value pair
    }
})
// Now, you can use it like this -> request.get() or request.post()
request.get('users')
.then(res => this.setState({ users: res.data, isLoading: false }))
.catch(err => this.setState({ err, isLoading: false }));

How to fetch data in React using async/await

If you used JavaScript Promises before in your code then it will more easy for you to understand async/await.
It's a special syntax to work with asynchronous requests in a more comfortable way. From async/await you can fetch data inside a React Component from API in a more precise way.

To use, you must include the async keyword before the function keyword. async word before function means that a function will always return a promise and await makes JavaScript wait until promise settled and return its results.

In the example below, I am going to use it along with axios to pull data.


async getUsers() {
let api_url = 'https://api.github.com/users';
    try {
    const res = await axios.get(api_url);
    this.setState({
    users: res.data,
    isLoading: false
})
} catch(err) {
    this.setState({
    err,
    isLoading: false
})
}
}
// and don't forget to bind getUsers function
// with component to access "this.setState" inside getUsers

How to Fetch data using React Hooks

In version 16.8.0 React added some new function called React Hooks. Hooks allow you to use state and other React features without writing a class.

What is Hook?

Hooks are functions that let you "hook into" React state and lifecycle features from function components. Hooks don't work inside classes, they let you use React without classes.
React provides some built-in Hooks like useState, useEffect, useContext, or either you can build your own custom hooks with "use" prefix.

In the example below, I am using useState and useEffect hook to pull data and render it into DOM.


import React, { useState, useEffect } from 'react';
let api_url = 'https://api.github.com/users';
// App component 
function App() {
    // Initialize state first
    let [users, setUsers] = useState([]);
    let [isLoaded, setIsLoaded] = useState(false);
    let [err, setErr] = useState(null);
    useEffect(() => {
    const getUsers = () => {
        fetch(api_url)
            .then(res => {
                // Unfortunately, fetch doesn't send (404 error)
                // into the cache itself
                // You have to send it, as I have done below
                if (res.status >= 400) {
                    throw new Error("Server responds with error!")
                }
                return res.json()
            })
            .then(users => {
                setUsers(users)
                setIsLoaded(true)
            },
            // Note: it's important to handle errors here
            // instead of a catch() block so that we don't swallow
            // exceptions from actual bugs in components
                err => {
                    setErr(err)
                    setIsLoaded(true)
                })
    };
    getUsers()
    }, [])

Then render users data array into DOM using return


if (err) {
    return <div> {err.message} </div>
} else if (!isLoaded) {
    return <div> Loading... </div>
} else {
    return (
    <ul>
        {
            users.map(user => (
                <li key={user.id}>
                    {user.login}
                </li>
            ))
        }
    </ul>
    )
}