4

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.

Matt
  • 791
  • 4
  • 13

3 Answers3

5

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

Florian Motteau
  • 3,467
  • 1
  • 22
  • 42
  • Thanks for the answer. Yes this does console.log it but still causes the app to crash with ```cannot read property of id``` on page refresh. If its a state update it works but otherwise still stuck with the same problem... :( – Matt Aug 18 '21 at 11:17
  • OK, what's the exact error ? cannot read property id of undefined ? How are you trying to use the data you fetched ? – Florian Motteau Aug 18 '21 at 11:19
  • sorry did not see the JSX in your answer ! – Florian Motteau Aug 18 '21 at 11:20
  • You should null check carouselTitle before using it : {carouselTitle && carouselTitle[1].id}. Edited my answer. – Florian Motteau Aug 18 '21 at 11:22
  • Same question as I asked Danial, why does that work? – Matt Aug 18 '21 at 11:27
  • I commented on Danial's answer : on first component render, carouselTitle is an empty array, because the HTTP request is still pending, so trying to access carouselTitle[0] will cause a Javascript error. The null check prevents this. – Florian Motteau Aug 18 '21 at 12:02
1

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.

Danial
  • 1,603
  • 2
  • 15
  • 24
  • This fixed it so thank you but why? Whats the logic behind that line of code? – Matt Aug 18 '21 at 11:24
  • 1
    In your version, you are trying to access an index on a value which is undefined until the request came back. So JavaScript throws an error. – Florian Motteau Aug 18 '21 at 11:26
  • @matt_roki2 As I said, you pass an empty array to `useState` and in first render, component returns first item of an array that has nothing – Danial Aug 18 '21 at 11:27
-1

You should trigger useEffect for run fetch function

useEffect(()=>{fetchData();},[carouselTitle])
karagoz
  • 135
  • 5