3

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!

The Renaissance
  • 431
  • 9
  • 16
  • 1
    setState is async method, you are printing the value just after the setting. You can do some task based on state value , you will see changes in ui. – Santosh Sagar Aug 05 '20 at 13:20
  • I am doing tasks, and not seeing changes in the UI. I didn't put that part of the code in because I hadn't thought it was relevant at first. I think I may have misidentified the question. – The Renaissance Aug 05 '20 at 13:33

2 Answers2

8

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

state is a local const. 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))

},[])
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • 1
    You're 100% right, and the new `console.log` shows the data there, but it's still not rendering in the UI. I'm going to keep the question here and accept the answer in case someone else is up against this problem, and probably open another question. Thanks! – The Renaissance Aug 05 '20 at 13:34
  • @TheRenaissance Did you ever solve your other question about the state not rendering in the UI? Lol I know it's been 2 years, but I don't see another related question in your profile, and I'm having the same problem right now. – penguin Jul 22 '22 at 05:09
  • @penguin I do seem to remember that it was solved, but I don't remember the details, unfortunately - I'll try to look around and see if I can figure out what happened, and get back to you if I can – The Renaissance Jul 24 '22 at 09:13
  • @TheRenaissance Thank you, but there is no need. I resolved it another way. Thanks for offering to check though! – penguin Jul 25 '22 at 19:24
2

Setting a state has some delay, so in order to get the value of your state right after setState('value'); is to use React.useEffect like so:

const [state, setState] = useState();

const handleState = () => {
    // Here we assume that user has clicked some button
    setState('bla bla bla...');

    // If I try to log the state here, I get undefined as the output
    console.log(state); // ** BAD PRACTICE **
}

React.useEffect(() => {
    console.log(state); // Output: 'bla bla bla...'
}, [state]); // Here I passed 'state' as dependency, whenever state changes, the body of useEffect will be called.

Think of useEffect as a callback for whatever you've passed as dependency.

In some cases you have multiple dependencies, so passing more array members is possible:

React.useEffect(() => {
    // do your job
}, [state, someOtherState]);

Hooman Limouee
  • 1,143
  • 2
  • 21
  • 43