199

So I have this:

let total = newDealersDeckTotal.reduce(function(a, b) {
  return a + b;
},
0);

console.log(total, 'tittal'); //outputs correct total
setTimeout(() => {
  this.setState({ dealersOverallTotal: total });
}, 10);

console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1'); //outputs incorrect total

newDealersDeckTotal is just an array of numbers [1, 5, 9] e.g. however this.state.dealersOverallTotal does not give the correct total but total does? I even put in a timeout delay to see if this solved the problem. any obvious or should I post more code?

canbax
  • 3,432
  • 1
  • 27
  • 44
The worm
  • 5,580
  • 14
  • 36
  • 49
  • 1
    http://stackoverflow.com/a/36087156/4453045 – Assan Jan 03 '17 at 15:17
  • 1
    Besides what is said in the answers, you are explicitly logging the value of the state, *before* you are calling `setState`. – Felix Kling Jan 03 '17 at 15:27
  • 1
    @FelixKling no I'm calling this.state _after_ I set it. I am logging a variable before. no? – The worm Jan 03 '17 at 15:34
  • Because of the timeout your `setState` is indeed executed after you log the state. I think what you meant to do in debugging was putting the `console.log` part inside the timeout, and the `setState` outside. – Fabian Schultz Jan 03 '17 at 15:36
  • @FabianSchultz can you explain one thing I'm not getting then. consider this code: `if(this.state.playersOverallTotal > 21){ console.log('bust'); this.setState({playerBusted: true}); }` when it gets to over 21, the log fires but the state does not change and then only changes once the number increments again. e.g. if it got to 24 it would not set the state but then if it got to 28 for example it would – The worm Jan 03 '17 at 15:58
  • Can't reproduce it, maybe ask as a seperate question. – Fabian Schultz Jan 03 '17 at 16:10
  • Does this answer your question? [setState doesn't update the state immediately](https://stackoverflow.com/questions/41278385/setstate-doesnt-update-the-state-immediately) – ggorlen May 24 '21 at 16:13

12 Answers12

311

setState() is usually asynchronous, which means that at the time you console.log the state, it's not updated yet. Try putting the log in the callback of the setState() method. It is executed after the state change is complete:

this.setState({ dealersOverallTotal: total }, () => {
  console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1');
}); 
Fabian Schultz
  • 18,138
  • 5
  • 49
  • 56
  • 3
    In addition to that, the OP explicitly logs the state value *before* they call `setState`. – Felix Kling Jan 03 '17 at 15:28
  • This works for me as well, in the past I have used this: `this.setState({someVar: newValue},function(){ console.log("force update}); ' but for some reason it was't worring any more, when I updated the code as describe above it work. any idea why? – Jozcar Jul 13 '17 at 15:38
  • 1
    @Jozcar Should work also, syntax wasn't right (missing parentheses): `this.setState({someVar: newValue},function(){ console.log("force update") });` – Fabian Schultz Jul 13 '17 at 18:55
  • https://imgur.com/Ku0OjTl Please tell me what should i do to get rid of this problem. – Johncy Sep 01 '18 at 07:20
  • This does not work if you use `useState` hook in a functional component. Use `useEffect` instead for an effect after rendering. – Hasan Sefa Ozalp Feb 03 '20 at 01:01
  • 1
    I had completely forgot the fact that it's async call and did all possible code changes except for this one and here you saved my brain from burning out. thanks – Hem M Nov 04 '20 at 13:22
  • I never see the output, in addition the key which I was trying to update becomes undefined. – Kacper Kwaśny Aug 20 '21 at 19:30
  • How do I do the same thing with a state management library such as zustand? – lool Mar 23 '23 at 17:30
45

In case of hooks, you should use useEffect hook.

const [fruit, setFruit] = useState('');

setFruit('Apple');

useEffect(() => {
  console.log('Fruit', fruit);
}, [fruit])
Siraj Alam
  • 9,217
  • 9
  • 53
  • 65
  • Great, it works with useEffect. But why does it need one? – alex351 Jul 09 '20 at 07:25
  • 1
    `useEffect` runs on every re-render, and if the items passed into the array are state variable sand changed. So when the fruit changed and component re-renders, that useEffect will run. – Siraj Alam Jul 09 '20 at 07:29
  • 1
    I run setFruit and console.log fruit outside of useEffect, and it does not change. :/ – alex351 Jul 09 '20 at 07:33
29

setState is asynchronous. You can use callback method to get updated state.

changeHandler(event) {
    this.setState({ yourName: event.target.value }, () => 
    console.log(this.state.yourName));
 }
Mahesh Joshi
  • 608
  • 8
  • 9
17

I had an issue when setting react state multiple times (it always used default state). Following this react/github issue worked for me

const [state, setState] = useState({
  foo: "abc",
  bar: 123
});

// Do this!
setState(prevState => {
  return {
    ...prevState,
    foo: "def"
  };
});
setState(prevState => {
  return {
    ...prevState,
    bar: 456
  };
});
Arun Gopalpuri
  • 2,340
  • 26
  • 27
16

The setState is asynchronous in react, so to see the updated state in console use the callback as shown below (Callback function will execute after the setState update)

this.setState({ email: 'test@example.com' }, () => {
   console.log(this.state.email)
)}
Mokesh S
  • 683
  • 6
  • 11
15

Using async/await

async changeHandler(event) {
    await this.setState({ yourName: event.target.value });
    console.log(this.state.yourName);
}
Dev01
  • 13,292
  • 19
  • 70
  • 124
14

The setState() operation is asynchronous and hence your console.log() will be executed before the setState() mutates the values and hence you see the result.

To solve it, log the value in the callback function of setState(), like:

setTimeout(() => {
    this.setState({dealersOverallTotal: total},
    function(){
       console.log(this.state.dealersOverallTotal, 'dealersOverallTotal1');
    });
}, 10)
Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
6

As well as noting the asynchronous nature of setState, be aware that you may have competing event handlers, one doing the state change you want and the other immediately undoing it again. For example onClick on a component whose parent also handles the onClick. Check by adding trace. Prevent this by using e.stopPropagation.

Gus T Butt
  • 197
  • 2
  • 4
  • Thanks for leaving this. Your comment pointed me in the right direction. I found the problematic code being updated in a conflicting event, then in that spot I used prevState callback to set new state as mentioned above by @Arun Gopalpuri ``` this.setState(prevState=>{ return {...prevState, newProperty }}); ``` – Gavin Sep 07 '21 at 10:53
6

If you work with funcions you need to use UseEffect to deal with setState's asynchrony (you can't use the callback as you did when working with classes). An example:

import { useState, useEffect } from "react";

export default function App() {
 const [animal, setAnimal] = useState(null);

 function changeAnimal(newAnimal) {
  setAnimal(newAnimal);
  // here 'animal' is not what you would expect
  console.log("1", animal);
 }

 useEffect(() => {
  if (animal) {
   console.log("2", animal);
  }
 }, [animal]);

 return (
  <div className="App">
  <button onClick={() => changeAnimal("dog")} />
 </div>
 );
}

First console.log returns null, and the second one returns 'dog'

Dani Alcalà
  • 212
  • 3
  • 5
5

I had the same situation with some convoluted code, and nothing from the existing suggestions worked for me.

My problem was that setState was happening from callback func, issued by one of the components. And my suspicious is that the call was occurring synchronously, which prevented setState from setting state at all.

Simply put I have something like this:

render() {
    <Control
        ref={_ => this.control = _}
        onChange={this.handleChange}
        onUpdated={this.handleUpdate} />
}

handleChange() {
    this.control.doUpdate();
}

handleUpdate() {
    this.setState({...});
}

The way I had to "fix" it was to put doUpdate() into setTimeout like this:

handleChange() {
    setTimeout(() => { this.control.doUpdate(); }, 10);
}

Not ideal, but otherwise it would be a significant refactoring.

zmechanic
  • 1,842
  • 21
  • 27
4

just add componentDidUpdate(){} method in your code, and it will work. you can check the life cycle of react native here:

https://images.app.goo.gl/BVRAi4ea2P4LchqJ8

Pravin Ghorle
  • 606
  • 7
  • 7
0

but in my case:

useEffect(() => {
if (pathname.includes('/moderation')) {
  if (pathname.includes('/approved')) {
    setStatus({ isAd: 1, isPublic: 1 });
    console.log(" approved status ", status, "path ", pathname);
  } else if (pathname.includes('/pending')) {
    setStatus({ isAd: 2, isPublic: 1 });
    console.log(" pending status ", status, "path ", pathname);
  } else {
    setStatus({ isAd: 0, isPublic: 1 });
    console.log(" all status ", status, "path ", pathname);
  }

}
console.log("Updated status:", status);}, [pathname]);

it always give previous value

  • This does not really answer the question. If you have a different question, you can ask it by clicking [Ask Question](https://stackoverflow.com/questions/ask). To get notified when this question gets new answers, you can [follow this question](https://meta.stackexchange.com/q/345661). Once you have enough [reputation](https://stackoverflow.com/help/whats-reputation), you can also [add a bounty](https://stackoverflow.com/help/privileges/set-bounties) to draw more attention to this question. - [From Review](/review/late-answers/34852343) – Miss Skooter Aug 18 '23 at 13:24