React Hooks: Loading Indicator and error handling
© https://reactjs.org/

React Hooks: Loading Indicator and error handling

Error handling when fetching data with useEffect

ByMario Kandut

honey pot logo

Europe’s developer-focused job platform

Let companies apply to you

Developer-focused, salary and tech stack upfront.

Just one profile, no job applications!

Short reminder what React Hooks are, and here useState and useEffect hooks in detail.

This blog article is about how to handle errors and loading indicators with useEffect. The article is a continuation of the example used in how to fetch data with React hooks.

💰 The Pragmatic Programmer: journey to mastery. 💰 One of the best books in software development, sold over 200,000 times.

Reminder: We want to fetch articles from hackernews.com with a specific topic and display the resulting articles in a list with links to the corresponding article. HackerNews has a search API, which is powered by Algolia and it can be queried. As a query string you can use whatever you want, I will use react. The HackerNews API is public, free, and well documented, Search Hacker News.

The code so far:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch(
        'https://hn.algolia.com/api/v1/search?query=react',
      );
      const json = await res.json();
      setData(json.hits);
    };
    fetchData();
  }, [setData]);

  return (
    <ul>
      {data.map(item => (
        <li key={item.ObjectId}>
          <a href={item.url}>{item.title}</a>
        </li>
      ))}
    </ul>
  );
}

export default App;

How to add a loading indicator

To display a loading spinner or similar we have to know the current state of data fetching.

So we just add another state hook (useState) to handle the isLoading state and,

const [isLoading, setIsLoading] = useState(false);

set the state of isLoading based on the data fetching.

// before fetching data
setIsLoading(true);
// when data is fetching
setIsLoading(true);

Now, let's add it to the overall code example.

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      const res = await fetch(
        'https://hn.algolia.com/api/v1/search?query=react',
      );
      const json = await res.json();
      setData(json.hits);
      setIsLoading(false);
    };
    fetchData();
  }, [setData]);

  return (
    <React.Fragment>
      {isLoading ? (
        <p>Loading ...</p>
      ) : (
        <ul>
          {data.map(item => (
            <li key={item.ObjectId}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </React.Fragment>
  );
}

export default App;

Code explanation: When the effect is called for data fetching (component mounts), the loading state is set to true. Once the request resolves, the loading state is set to false again.

How to handle errors when fetching data

Proper handling of errors should be considered in every project, since the server could be not responding (maintenance, hardware problems, ...) or the request is missing some parameters or... . Think of error handling as a mandatory item on your project todo list.

Error handling with useEffect is just another state, hence another useState hook. We set the error state, when an error occurs. This is usually done in a try/catch statement, when working with async/await. You can also add the error message response from the API to your error state, for this example it will be just a boolean flag.

We add the useState for hasError and

const [hasError, setHasError] = useState(false);

set the state in the try/catch statement.

const fetchData = async () => {
  setIsLoading(true);
  setHasError(false);
  try {
    const res = await fetch(
      'https://hn.algolia.com/api/v1/search?query=react',
    );
    const json = await res.json();
    setData(json.hits);
  } catch (error) {
    setHasError(true);
  }
  setIsLoading(false);
};

Now let's combine this to the overall code example:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [hasError, setHasError] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      setHasError(false);
      try {
        const res = await fetch(
          'https://hn.algolia.com/api/v1/search?query=react',
        );
        const json = await res.json();
        setData(json.hits);
      } catch (error) {
        setHasError(true);
      }
      setIsLoading(false);
    };
    fetchData();
  }, [setData]);

  return (
    <React.Fragment>
      {hasError && <p>Something went wrong.</p>}
      {isLoading ? (
        <p>Loading ...</p>
      ) : (
        <ul>
          {data.map(item => (
            <li key={item.ObjectId}>
              <a href={item.url}>{item.title}</a>
            </li>
          ))}
        </ul>
      )}
    </React.Fragment>
  );
}

export default App;

Code explanation: The error state is reset every time the useEffect hook runs (component mounts).

TL;DR

  • Loading indicators increase UX and are easy to implement with useState.
  • Error handling should be a mandatory step in your project.
  • Error handling can be done easily with a useState hook.

Thanks for reading and if you have any questions, use the comment function or send me a message @mariokandut.

If you want to know more about React, have a look at these React Tutorials.

References (and Big thanks):

ReactJS, Dave Ceddia, Robin Wieruch

Scroll to top ↑