React useState hook not updating with axios call

Viewed 2576

I am aware this is a common question as I have spent the last two hours going through every answer and trying to get my state to update but nothing is working.

I am fetching text from a cms however on first load the state is undefined and my app crashes. However if I comment the line out, load the page and uncomment the line the correct values are displayed.

Here is some of the code I have tried.

The data i am hoping to get

[
  {id:1},
  {id:2},
  {id:3},
  {id:4},
]
import react, {useEffect, useState} from 'react'
import axios from 'axios'

const [carouselTitle, setCarouselTitle] = useState([])

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


    const fetchData = async () => {
        await axios('api').then(
            response => {
                console.log(response.data)
                setCarouselTitle(response.data)
                console.log(carouselTitle)
            })
      };
return(
  <h1>{carouselTitle[1].id}</h1>
)

console logging the data works fine but when i console log the state it does not work.

2ND METHOD I TRIED

    useEffect(() => {
        const fetchData = async () => {
         const res = await axios('api');
         const carouselTitleAlt = await res.data;
         setCarouselTitle({ carouselTitleAlt });
         console.log(carouselTitleAlt);
         console.log(carouselTitle);
       };
       fetchData();
      }, []);

Again console logging the const inside the useEffect displays the correct information but logging the state does not work.

Appreciate your responses or better ways of displaying the data.

3 Answers

setState is asynchronous : https://reactjs.org/docs/faq-state.html#why-doesnt-react-update-thisstate-synchronously

It means that you cannot expect to console.log the new state value the line after you called setCarouselTitle.

To log the new value, you could use another useEffect, with carouselTitle in the dependencies array, where you console.log(carouselTitle) :

useEffect(() => {
  console.log(carouselTitle);
}, [carouselTitle]);

That said, your component should behave correctly, it will be refreshed when the state is updated.

In the JSX you should check that carouselTitle is not undefined (meaning that the request failed or is still pending) :

{carouselTitle && <H1>{carouselTitle[0].id}}

https://reactjs.org/docs/conditional-rendering.html#gatsby-focus-wrapper

First of all, if you pass an empty array for initial data to useState, you can't get any item in that array in here:

return(
  <h1>{carouselTitle[1].id}</h1>
)

Because component returns first item of an array that has nothing. I prefer to you do it like this:

return(
  <h1>{carouselTitle.length > 0 && carouselTitle[0].id}</h1>
)

And also based on this and official documentation, setState (and also setSomthing() using useState()) is asynchronous.

So state data doesn't show immediately after setting that.

You should trigger useEffect for run fetch function

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