DEV Community

Alessio Michelini
Alessio Michelini

Posted on • Updated on

Fetching data with React hooks and Axios

I have to be honest that I was getting a bit rusty with React lately, I've been working on mostly backend and CLIs stuff for the past few months, and as I have a very bad memory I tend to forget how things, I used not so long ago, works.

As now I have to work on the front facing part of the application, I need to fetch information from the API and display them, a solution could be to run something like this:

// mycomponent.js
import React, { useEffect, useState} from 'react';
import axios from 'axios';

const MyComponent = () => {
  const [loading, setLoading] = useState(true);
  const [data, setData] = useState([])

  useEffect(() => {
    const fetchData = async () =>{
      setLoading(true);
      try {
        const {data: response} = await axios.get('/stuff/to/fetch');
        setData(response);
      } catch (error) {
        console.error(error.message);
      }
      setLoading(false);
    }

    fetchData();
  }, []);

  return (
    <div>
    {loading && <div>Loading</div>}
    {!loading && (
      <div>
        <h2>Doing stuff with data</h2>
        {data.map(item => (<span>{item.name}</span>))}
      </div>
    )}
    </div>
  )
}

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

So essentially we tell the component that when it mounts, it should call the fetchData function to populate our data array from the API, and we put some conditions to not show anything while we area loading our data.

The code above is fine as it is, but it stores a bit of logic in the component itself.

If you need to reuse the same logic in another component, that perhaps renders the same data but in a different way, you need to duplicate it, making the code not very DRY.

Custom hooks FTW

I firmly believe that abstraction is always a good way to go, and in my opinion a better solution is to create a custom react hook, where essentially we move the logic of fetching the data to another file, and we make it a reusable hook that can be called from multiple components if needed.

The code for the hook will be something like this:

// use-fetch-data.js
import { useEffect, useState} from 'react';
import axios from 'axios';

const useFetchData = () => {
  const [data, setData] = useState({});
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const { data: response } = await axios.get('/stuff/to/fetch');
        setData(response);
      } catch (error) {
        console.error(error)
      }
      setLoading(false);
    };

    fetchData();
  }, []);

  return {
    data,
    loading,
  };
};

export default useFetchData;
Enter fullscreen mode Exit fullscreen mode

Note: for the sake of keeping the code short I didn't manage the state for errors, relying just on console.error.

Now we can refactor our component code, removing all the logic and the states we no longer need, to a much shorter code:

//mycomponent.js
import React from 'react';
import useFetchData from './hooks/use-fetch-data.js'

const MyComponent = () => {
  const {
    data,
    loading,
  } = useFetchData();

  return (
    <div>
    {loading && <div>Loading</div>}
    {!loading && (
      <div>
        <h2>Doing stuff with data</h2>
        {data.map(item => (<span>{item.name}</span>))}
      </div>
    )}
    </div>
  )
}

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

I hope it will help you understanding better React hooks with this practical example.

Latest comments (7)

Collapse
 
aoda-zhang profile image
aoda • Edited

First, that is great for create a fetch data hooks to get the data, then other pages can re-use it , but i think there can upgrade more, can pass the endpoint url , then that would be a more powerful common fetch data hooks.
if interested,can refer to this article smashingmagazine.com/2020/07/custo...

Collapse
 
alirzea98u profile image
Alireza

das ist so praktisch

Collapse
 
fishme profile image
App Evangelist • Edited

your last example can't work.
const [loading, setLoading] = useState(false);
otherwise the loading state is always false.

have to be
const [loading, setLoading] = useState(true);

Collapse
 
darkmavis1980 profile image
Alessio Michelini

You are right, I probably copied over from an example to another, thanks for that

Collapse
 
r11 profile image
Peter Jaffray

Thanks for the tips on abstraction. This is exactly where I am in my learning and seeing how you made a custom hook levelled me up :) Cheers!

Collapse
 
victorocna profile image
Victor Ocnarescu

Cool example! One thing to add that I would find useful is a status returned from the custom hook instead of the loading boolean. This way the status can be success, error or loading and you can also handle Axios errors.

Cheers!

Collapse
 
darkmavis1980 profile image
Alessio Michelini

That's a really good idea, I just kept it simple for the sake of not writing too much code and just get the gist, but yeah, you are totally right