reactjssetstateuse-effect

React: state contains empty object after setState on object with values


There are certainly a lot of questions and answers about setState, and I've tried looking at them, but haven't found a problem (or solution) that resembled mine closely enough to be useful. It's still very possible that there is one out there that deals with this, and if so, please point it out, and apologies for the duplicate.

Here's the relevant code from a functional React component:

const [ state, setState ] = useState({})

useEffect(data => {
     const getFromServer = axios.get('http://localhost:3030/poolInfo')
        .then(res => {
            console.log("RES.DATA LOOKS LIKE THIS:, ", res.data);
            setState(res.data);  // I also tried setState({...res.data})
            console.log("IN AXIOS, STATE IS NOW: ", state);
        })
        .catch (err => console.error("YO YOU GOT AN ERROR IN AXIOS ", err))

},[])

Here are the results of the two console.logs above:

RES.DATA LOOKS LIKE THIS:, Object { balance: 1000000000000000000, feeAndSplit: Array [500, 20] }

IN AXIOS, STATE IS NOW: Object { }

The first console.log shows the data exactly like I would expect it. (Hitting the same API with Postman returns the same data, too.) I call setState on literally the same variable that just had data a second ago in the logs, and poof! Now it's an empty object.

I thought maybe the console.log itself was doing something weird to res.data, so I also tried commenting out the first console.log, but still got the same results.

I've wondered if maybe this has something to do with setting state inside useEffect, but if it does, I'm at a loss. (For example, this answer seems to indicate that as long as an empty array is passed in at the end of useEffect, everything should be fine and dandy.)

What happened to res.data, and/or how can I set the state to it?

Thanks!


Solution

  • Everything's working, except you've put your log statement in a place where it's not useful.

    state is a local const, with a snapshot of the state at time of rendering. It will never change, and that's not what setState is trying to do. The purpose of calling setState is to tell react to rerender the component. When the component rerenders, a new local const will be created, which will have that new value. Code in the new render can access that new value, but code in the old render is still referring to the previous value.

    So if you'd like to verify that it's rerendering with the new value, put the log statement in the body of the component so it can log when rendering:

    const [ state, setState ] = useState({})
    console.log("Rendering with: ", state);
    
    useEffect(data => {
         const getFromServer = axios.get('http://localhost:3030/poolInfo')
            .then(res => {
                console.log("RES.DATA LOOKS LIKE THIS:, ", res.data);
                setState(res.data);
            })
            .catch (err => console.error("YO YOU GOT AN ERROR IN AXIOS ", err))
    
    },[])