Aldor
Aldor

Reputation: 292

Unable to access properties of object stored in a React state

I am using React hooks and I need to store the data from my server which is an object, to a state.

This is my state:

  const [sendingTime,setSendingTime] = useState({})

This is how I set it inside useEffect:


    const getTime= () =>{
        axios.get("https://localhost:1999/api/getLastUpdatedTime")
            .then(res => {

                console.log(res.data)

                setSendingTime({sendingTime : res.data})
          
                console.log('sendingTime is coimng',sendingTime)

            })
            .catch(err => console.error(err))
    }


 useEffect(() => {
        getCount()
        getTime()
    },[])

Now when I console.log the object state it returns an empty object. Although if I set the object to any variable and then console.log it it doesn't return an empty object. But both ways I am unable to access the properties of the object.

Edit

This is what I was doing previously:

 const[time,setTime] = useState({
        totalComplaintsTime: '00:00',
        resolvedComplaintsTime: '00:00',
        unresolvedComplaintsTime:'00:00',
        assignedComplaintsTime:'00:00',
        supervisorsTime:'00:00',
        rejectedComplaintsTime:'00:00'

    })

  const getTime= () =>{
        axios.get("https://localhost:1999/api/getLastUpdatedTime")
            .then(res => {

                console.log(res.data)

                setTime({
                    totalComplaintsTime: res.data.Complaints[0].updatedAt,
                    resolvedComplaintsTime:  res.data.Resolved[0].updatedAt,
                    unresolvedComplaintsTime: res.data.Unresolved[0].updatedAt ,
                    assignedComplaintsTime: res.data.Assigned[0].updatedAt ,
                    rejectedComplaintsTime: res.data.Rejected[0].updatedAt,
                    supervisorsTime: res.data.Supervisors[0].updatedAt
                })

            

            })
            .catch(err => console.error(err))
    }

 useEffect(() => {

        getCount()

        // getTime()

        getTime()
    },[])

And this is how I used the states to set the dynamic values :

Last updated at {time.resolvedComplaintsTime}

This is working perfectly fine but I wanted to store the data in an object state and then access it, which would've been easier and more efficient. Also I wanted to pass this object to another component. That is why I wanted to make a state and store the data in that state.

Solved

So the main problem was accessing the data throughout the component. This is the solution:

sendingTime is being initialized but only when a render occurs. So we add a piece of code to check if that state is initialized or not.

This is where I wanted to display the data.

 <div key={sendingTime.length}  className={classes.stats}>
            <UpdateIcon fontSize={"small"} /> Last updated at{" "}
              {Object.keys(sendingTime).length > 0 &&
              sendingTime.Complaints[0].updatedAt}
          </div>

This way I can access the properties of the object stored in the sendingTime state very easily.

Upvotes: 0

Views: 91

Answers (3)

Dana Woodman
Dana Woodman

Reputation: 4522

The other answers are correct, setState is asynchronous so you will only be able to get sendingTime's new value on the next re-render. And as @Enchew mentions, you probably don't want to set an object as that value most likely.

Try this instead:

const [data, setData] = useState(undefined)

const getTime = () => {
  axios.get("https://localhost:1999/api/getLastUpdatedTime")
    .then(({ data }) => {
      console.log(data)
      setData(data)
    })
    .catch(err => console.error(err))
}

useEffect(() => {
  getCount()
  getTime()
}, [])

if (!data) return <p>No data...</p>

return (
  <>
    <p>Complaints: {data.Complaints[0].updatedAt}<p>
    <p>Resolved: {data.Resolved[0].updatedAt}<p>
    <p>{/* ...etc... */}</p>
  </>
)

You should see the value you're expecting to see in the console.log because you're using the locally scoped variable, not the asynchronous value, which will only be updated on the next re-render.

Upvotes: 1

Enchew
Enchew

Reputation: 1001

setSendingTime works asynchronously and your object may have not been saved in the state yet. Also pay attention to the way you save the time in your state, you are wrapping the result of the data in another object with property sendingTime, which will result in the following object: sendingTime = { sendingTime: {/*data here */} }. If you are running for sendingTime: {/*data here */} try the following:

const getTime = () => {
  axios
    .get('https://localhost:1999/api/getLastUpdatedTime')
    .then((res) => {
      console.log(res.data);

      setSendingTime(res.data);

      console.log('sendingTime is coimng', sendingTime);
    })
    .catch((err) => console.error(err));
};

Also have a look at this - how to use hooks with a callback: https://www.robinwieruch.de/react-usestate-callback

Upvotes: 0

HermitCrab
HermitCrab

Reputation: 3274

setSendingTime comes from a useState so it is asynchronous:

When you call:

setSendingTime({sendingTime : res.data})
console.log('sendingTime is coimng',sendingTime)

The state sendTime has not been updated, so it display the init value which is {}

Upvotes: 1

Related Questions