Course: Section

Get Started with Modern React: Learn by Doing

Episode: Title

S03・V12: Setting State

Date Created: July 17th, 2019
Last Updated: 27 days ago

Objective
  • We will show you how to set values in our `squares` state array.
Watch Video
Duration: 3m 41s

Setting State

As a next step, when we click on a square, we want to show an X. This entails updating our state variable squares in the Board component, so that the element representing the Square clicked, changes its value from null to X.

In React, we should not mutate state directly, for reasons we will explain in more detail in the next video. We will first make a copy of the squares array, then make changes to the copy, and finally switch out the current state with the modified state.

Let’s add comments in our handleClickEvent function breaking down this process.

  const handleClickEvent = i => {
    // Make a copy of `squares` array

    // Mutate the copy, setting the i-th element to `'X'`

    // Set the `squares` state to be the mutated copy

  };

Let’s make a copy of the squares array using “array spread syntax”, and assign the copy to a variable we will call newSquares.

  const handleClickEvent = i => {
    // Make a copy of `squares` array
    const newSquares = [...squares];    // Mutate the copy, setting the i-th element to `'X'`

    // Set the `squares` state to be the mutated copy

  };

As a second step, let’s update the copy, setting the clicked element to 'X'.

  const handleClickEvent = i => {
    // Make a copy of `squares` array
    const newSquares = [...squares];
    // Mutate the copy, setting the i-th element to `'X'`
    newSquares[i] = 'X';    // Set the `squares` state to be the mutated copy

  };

Finally, let’s use our state setter called setSquares, defined in our useState hook, to set the state to be the modified copy.

  const handleClickEvent = i => {
    // Make a copy of `squares` array
    const newSquares = [...squares];
    // Mutate the copy, setting the i-th element to `'X'`
    newSquares[i] = 'X';
    // Set the `squares` state to be the mutated copy
    setSquares(newSquares);  };

Now test this out in the browser. We will first click on the square with index 2. Now we will click on the square with index 7. Our UI is updating as expected, showing an 'X' when we click on a square.

We have successfully set the squares state array on the Board. Before we move on to the next video, let’s do some minor refactoring. Our initialSquares array can be simplified.

  const initialSquares = [
    null, null, null,
    null, null, null,
    null, null, null,
  ];
  const initialSquares = Array(9).fill(null);

This is a shorter way of creating an array of 9 elements and making them all null. Now, we can delete the longer definition of initialSquares. Next, we will remove the comments.

Finally, we will update the value passed to onClick in the Square component’s button, removing the superfluous arrow function. We can do this because () => props.onClickEvent() is equivalent to props.onClickEvent.

const Square = props => {
  return (
    <button
      className="square"
      onClick={props.onClickEvent}    >
      {props.value}
    </button>
  );
};

Code Snapshot

src/index.js
import React, { useState } from 'react';
import ReactDOM from 'react-dom';

import './index.css';

const Square = props => {
  return (
    <button
      className="square"
      onClick={props.onClickEvent}
    >
      {props.value}
    </button>
  );
};

const Board = () => {
  const initialSquares = Array(9).fill(null);
  const [squares, setSquares] = useState(initialSquares);

  const handleClickEvent = i => {
    const newSquares = [...squares];
    newSquares[i] = 'X';
    setSquares(newSquares);
  };

  const renderSquare = i => {
    return (
      <Square
        value={squares[i]}
        onClickEvent={() => handleClickEvent(i)}
      />
    );
  };

  return (
    <div style={{
      backgroundColor: 'skyblue',
      margin: 40,
      padding: 20,
    }}>
      Board
      <div className="board-row">
        {renderSquare(0)}
        {renderSquare(1)}
        {renderSquare(2)}
      </div>
      <div className="board-row">
        {renderSquare(3)}
        {renderSquare(4)}
        {renderSquare(5)}
      </div>
      <div className="board-row">
        {renderSquare(6)}
        {renderSquare(7)}
        {renderSquare(8)}
      </div>
    </div>
  );
};

const Game = () => {
  return (
    <div className="game">
      Game
      <Board />
    </div>
  );
};

ReactDOM.render(
  <Game />,
  document.getElementById('root')
);

Summary

We showed the 3-step process to correctly update a state array. We first copied the array, then we mutated the copy, and finally we set the state array to be the mutated copy.

Next Up…

In the next video, we will explain why this 3-step process for updating non-scalar state is necessary in React.