Pierre
Pierre

Reputation: 41

React - issue with useEffect() and axios.get

I am struggling to understand why my code is not working. I am using the useEffect() hook to make a call to an API, and then I am using setState() to update my component state. In my JSX, I am mapping my info array to render the data.

Here's my code:

import React, { useState, useEffect } from 'react';
import axios from 'axios';
import './App.css';

function App() {
    const [info, setInfo] = useState();
    console.log(info);

    useEffect(() => {
        const getUsers = async () => {
            const res = await axios('https://api.mocki.io/v1/b043df5a');
            console.log(res.data);
            setInfo(res.data);
        };

        getUsers();
    }, []);

    return (
        <div>
            <input type='text' placeholder='Search users' />
            <input type='text' placeholder='Search users' />

            {info.map((el, index) => {
                console.log(el);
                return <h1 key={index}>{el.city}</h1>;
            })}
        </div>
    );
}

export default App;

However, I get this error: 'TypeError: Cannot read property 'map' of undefined'. My best guess is that my JSX is rendered before my state is populated with API info.

The code does work when I write the following JSX, which allows me to check if 'info' is true:

{info && info.map((el, index) => {
  console.log(el);
  return <h1 key={index}>{el.city}</h1>;
})}

Is this a normal behavior? Why is useEffect not populating my state before my page is rendered?

I would appreciate your help, as I am struggling to find the solution to this specific issue.

Thanks!

Upvotes: 4

Views: 6928

Answers (5)

Jolan
Jolan

Reputation: 364

A use Effect with a [] as second can be interpreted as a 'componentDidMount'

To give a very simple answer, your code is 'executed' the first time without the useEffect. So indeed, 'info' will not exist. At this point your 'info' variable is not yet defined. Then, when your component 'is mounted', it will execute the code in your useEffect. Only then info will be filled in.

I would recommend to go through this documentation to fully understand this: https://reactjs.org/docs/state-and-lifecycle.html

Upvotes: 0

Justinas
Justinas

Reputation: 26

So basicly useEffect function is equivalent of componentDidMount. What I'm trying to say your component renders and then it executes function passed in useEffect. To avoid this eighter use check that you introduced as a fix yourself or pass default value to useState method. I would suggest your option but with condition if info exists show it and if it's not then show some kind of loading indicator.

Upvotes: 0

Freestyle09
Freestyle09

Reputation: 5508

You are trying to iterate over undefined it's normal cause you need to check first before data will come to your state. You can achieve this in two ways:

info && info.map

But your initial state must be falsy value like:

useState(null)

Or better way to set initial state to empty array and map will run when data will come and you will get no error.

Upvotes: 0

Taghi Khavari
Taghi Khavari

Reputation: 6582

that's because useEffect hook will run after the dom render phase finished and one more thing that can cause the delay of getting data is the fact that you're calling an asynchronous function which usually get some time to finished.

so what are the possible options here:

  1. just use empty array [] as default value

  2. check the length of the state like info.length && whatever...

  3. sometimes you can use the useLayoutEffect which is kinda a synchronous operation. but in your case which is an api calls solution 1 and 2 is the answer

Upvotes: 1

FireFighter
FireFighter

Reputation: 706

Just do this: const [info, setInfo] = useState([]);

The issue is that you have no intial value and therefore it automatically defaults to undefined. Now you are trying to call .map on a value that is undefined therefore it throws an error. With an empty array as the initial value however, .map will loop over an empty array on the first render (before the useEffect) and it won't throw any error.

Upvotes: 1

Related Questions