0

When I increment, the alert gives the real numRef.current value, but the value of stateNumber is always the previous one.

I understand that this is due to closure, but even though I've been reading about how scopes and closure work, I don't figure out why in this case the value of stateNumber is not the current one since setStateNumber is called before setTimeout, so the value should be already updated

import React, { useState, useEffect, useRef } from "react";

const RefComponent = () => {
  const [stateNumber, setStateNumber] = useState(0);
  const numRef = useRef(0);

  function incrementAndDelayLogging() {
    setStateNumber(stateNumber + 1);
    numRef.current++;
    setTimeout(
      () => alert(`state: ${stateNumber} | ref: ${numRef.current}`),
      1000
    );
  }

  return (
    <div>
      <h1>useRef Example</h1>
      <button onClick={incrementAndDelayLogging}>delay logging</button>
      <h4>state: {stateNumber}</h4>
      <h4>ref: {numRef.current}</h4>
    </div>
  );
};

export default RefComponent;
skyboyer
  • 22,209
  • 7
  • 57
  • 64
Nour
  • 41
  • 1
  • 2
  • 1
    `set` is async.. you should try logging your value in `useEffect` which is equivaluent to componentDidUpdate – Panther Jul 21 '19 at 17:46
  • Its because react states are updated asynchronously by the time when alert triggered it remember its previous state(as it is asynchronous) and the alert have the scope of the previous one. So it prints the previous one – Shubham Verma Jul 21 '19 at 17:48

1 Answers1

0
function incrementAndDelayLogging() {
const newStateNumber = stateNumber + 1
setStateNumber(newStateNumber);
numRef.current++;
setTimeout(
  () => alert(`state: ${newStateNumber} | ref: ${numRef.current}`),
  1000
);

}

This would probably fix it :)