Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Official React Tutorial, using Hooks - Component Not Updating

I started recreating the official React Tutorial using hooks.

I have encountered two issues I am struggling to solve.

Please refer to the CodePen for the full code, including line numbers.

Full Code

CodePen: React Tutorial with Hooks

Question 1

// Click handler
function handleClick(e) {
  e.preventDefault();
  // console.log('1) Inside Square click handler');
  props.onUpdate();

  // Logs un-updated value, probably because setState is async
  console.log("props.value", props.value);

  // STACKOVERFLOW QUESTION (Line 18)
  // This still logs un-updated value (null). Why?
  // PS: Line 23 logs the right value
  window.setTimeout(() => {
    console.log("props.value after timeout", props.value);
  }, 1000);
}

// Logs correct value (Line 23)
console.log("props.value outside", props.value);

Line 18 logs a passed prop from within a handler, in a setTimeout. I am aware that state is set asynchronously, in line 9 (callback from parent). I understand that line 12 logs the value prior to the async state update, but I do not understand why the timeout still prints the wrong value.

Question 2

<div className = "status" >
  {
    (() => {
      console.log("rendering, status:", status);
      return status;
    })()
  }
</div>

Why is status in line 75 and 76 not reflecting the latest state change? I set it in line 62, then directly afterwards update state, which in return should cause a re-render with the right value. I am guessing the answer is that re-renders runs the entire function component again, resetting status (i.e. only state is saved). If that is right, how do we know exactly which components re-render on each state / prop / context update?

like image 639
Magnus Avatar asked Sep 13 '26 16:09

Magnus


1 Answers

I am no expert so could be wrong a bit.

Question 1

why the timeout still prints the wrong value.

Function Components (FC) are different from Class Components (CC) in a way that, 1. In FC, props contains the snapshot value after the render (using JavaScript closure) 1. In CC, props contains the updated value even after the render (because this.props can be changed as this is not immutable).

I've just read How Are Function Components Different from Classes?, yesterday and that's how the "wrong" values, null are showing up.

Initially, score array is [null x 9] thus props.value is set to null.

function Board() {
  const [isXNext, setIsXNext] = React.useState(true);

  // Initialize array with null values
  const initialArray = Array(9).fill(null);
  const [score, setScore] = React.useState(initialArray);

  // onUpdate handler includes index, thus have to be arrow function
  function renderSquare(i) {
    return <Square value={score[i]} onUpdate={() => handleUpdate(i)} />;
  }

...
}

And that null is closed over to window.setTimeout so props.value still has null after the time out.

  function handleClick(e) {
    ...
    window.setTimeout(() => {
      console.log("props.value after timeout", props.value);
    }, 1000);
  }

Question 2

Why is status in line 75 and 76 not reflecting the latest state change?

status is a local variable not a React state.

let status = `Next player: ${getPlayer()}`;

Mutating the status like the following won't re-render as React doesn't track the variable.

status = "Winner: " + winner;

Maybe you can declare the status as a state, like

const [status, setStatus] = React.useState(`Next player: ${getPlayer()}`);

And update the status using setStatus

if (winner) {
  setStatus("Winner: " + winner);
  console.log("game-won status:", status);
}

As mentioned, I've only read Dan's article yesterday so could not be 100% accurate.

like image 54
dance2die Avatar answered Sep 15 '26 04:09

dance2die