Course: Section

Get Started with Modern React: Learn by Doing

Episode: Title

S03・V14: Taking Turns

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

Objectives
  1. We will show alternating `X`s and `O`s on the board.
  2. We will show the status of which player has the next move.
Watch Video
Duration: 3m 46s

Taking Turns

As our next step, we want to show an O on our board after showing an X. We will create a state variable called xIsNext in our Board component. This variable takes a boolean value. We’ll set the first move to be X by default. So, the initial value of the xIsNext state variable is true. We will use the useState hook to declare the xIsNext state variable, along with its setter function, setXIsNext.

  const initialSquares = Array(9).fill(null);
  const [squares, setSquares] = useState(initialSquares);
  const [xIsNext, setXIsNext] = useState(true);

Each time a player moves, the boolean xIsNext will be flipped to determine which player goes next.

  const initialSquares = Array(9).fill(null);
  const [squares, setSquares] = useState(initialSquares);
  const [xIsNext, setXIsNext] = useState(true);

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

With this change, we can now take turns. We will try it in the browser. First click on the square with index 0. The first move should be an X, so that is what we want. Then click on the square with index 2. We expected the second move to be an O, which is what we got. Now click on the square with index 8, and then on index 6. The Xs and Os are alternating correctly. We are successfully taking turns.

Game Status

Let’s also add some status text in the Board that displays which player has the next turn.

  const initialSquares = Array(9).fill(null);
  const [squares, setSquares] = useState(initialSquares);
  const [xIsNext, setXIsNext] = useState(true);

  const handleClickEvent = i => {
    const newSquares = [...squares];
    newSquares[i] = xIsNext ? 'X' : 'O';
    setSquares(newSquares);
    setXIsNext(!xIsNext);
  };

  const status = `Next player: ${xIsNext ? 'X' : 'O'}`;

Let’s add the status text in to the returned JSX.

  return (
    <div style={{
      backgroundColor: 'skyblue',
      margin: 40,
      padding: 20,
    }}>
      <div>{status}</div>      <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>
  );
};

You can see the status text in the browser.

Styling the Status Text

We will now make some styling improvements. We will first remove the skyblue background color, and the margin and spacing as well. Now, we will add a CSS class to the div around the status text, for CSS styling. In the Game component, we will remove the text Game above <Board />.

  return (
    <div>
      <div className="status">{status}</div>      <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">
      <Board />
    </div>
  );
};

We will now add the CSS styling for the status class in the index.css file. We will add some margin.

src/index.css
.status {
  margin: 20px;
}

Test the changes in the browser.

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 [xIsNext, setXIsNext] = useState(true);

  const handleClickEvent = i => {
    const newSquares = [...squares];
    newSquares[i] = xIsNext ? 'X' : 'O';
    setSquares(newSquares);
    setXIsNext(!xIsNext);
  };

  const status = `Next player: ${xIsNext ? 'X' : 'O'}`;

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

  return (
    <div>
      <div className="status">{status}</div>
      <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">
      <Board />
    </div>
  );
};

ReactDOM.render(
  <Game />,
  document.getElementById('root')
);
src/index.css
body {
  background-color: #444;
  color: white;
  margin: 20px;
  font: 32px "Century Gothic", Futura, sans-serif;
}

.game {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.board-row {
  display: flex;
  flex-flow: row nowrap;
}

.square {
  background-color: #444;
  color: white;
  border: 1px solid #999;
  padding: 0;
  font-size: 84px;
  text-align: center;
  width: 100px;
  height: 100px;
}

.square:focus {
  outline: none;
  background-color: #222;
}

.status {
  margin: 20px;
}

Summary

We enabled alternate moves between the two players of the game, and we showed the status in the UI.

Next Up…

In the next video, we will calculate the winner of the game.