18

This is a popular question among all the new react developers but somehow I'm not able to understand the logic behind available solutions. I'm trying to update the state variable using hooks and trying it read the updated value but always it returns a previous value instead of new value. Below is the sequence of my code execution.

onClick={setTransactionAccountId}

on button click, it executes the below code and updates the state but the console.log shows the old value.

const [accountId, setAccountId] = useState(0);

const setTransactionAccountId = e => {
  console.log("Clicked ID:", e.currentTarget.value);
  setAccountId(e.currentTarget.value);
  console.log("accountId:", accountId);
};

console log:

  1. first button click:

Clicked ID: 0 accountId: 0

  1. second button click:

Clicked ID: 1 accountId: 0

could anyone please tell me the reason behind this behaviour and how to tackle it.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
vivek.p.n manu
  • 286
  • 1
  • 4
  • 19

3 Answers3

7

accountId won't have been updated this render. You have to wait for the next render for it to be updated. accountId only gets populated at the top of the function component when useState is called. You're in the middle of the render. If you need the actual value, keep pulling it out of e.currentTarget.value.

zero298
  • 25,467
  • 10
  • 75
  • 100
  • I just want to pass the accountId as props to another component hence im updating it so, as per my understanding by reading your comment i can always go and pass the accountId as props which will have latest value after button click but it just renders the old value. is my understadning correct ? – vivek.p.n manu Feb 28 '20 at 04:28
6

From react docs

React may batch multiple setState() calls into a single update for performance.

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

State updates may be batched and updated asynchronously, therefore the state may have not updated when console.log() is called. You will get the guaranteed updated result in your next call to useEffect hook.

Hassaan Tauqir
  • 2,464
  • 1
  • 13
  • 11
-2

This is because setState is asynchronous. Executing console.log('accountId:', accountId) before the state has updated will still give you the previous state value. If you add an async/await, that should fix the issue.

const setTransactionAccountId = async (e) => {
    console.log('Clicked ID:', e.currentTarget.value)
    await setAccountId(e.currentTarget.value)
    console.log('accountId:', accountId)
}
Andrea
  • 15
  • 3
    `setAccountId` doesn't return a Promise. So it's a race-condition which is really bad in this case. It's also completely useless since you already have the value of `accoundId` since you've just set it, `e.currentTarget.value`. – Emile Bergeron Feb 27 '20 at 17:33
  • Yes I do agree with @EmileBergeron and i tried the solution you shared by Andrea but i do see the old value getting printed in console. – vivek.p.n manu Feb 28 '20 at 04:35