Skip to main content
  • Home
  • Blog
  • Sandbox

Alvin Pascoe

Website Developer

Alvin Pascoe
Loading.....
  • React Website Tutorial - Improvement 4
    React Website Tutorial - Improvement 4
  • The Beauty of Code
    The Beauty of Code

React Website Tutorial - Improvement 5

Update: The file in this tutorial is now available on GitHub: When someone wins, highlight the three squares that caused the win.

3rd February, 2017

Over the last few months the creators of React have been updating their docs with ES6 compatible code. They've also updated their tutorial, which includes suggestions for a few extra improvements. This article describes how I made one of those improvements.

When someone wins, highlight the three squares that caused the win.

In order to check which three squares caused the win, I edited the calculateWinner() helper function to return an object containing the winning line, in addition to the winning shape (which it already does).

Next, the winning line (if it exists) is passed as one of the Board component's props. In the renderSquare() method, we use this winning line prop and check if the current square index i appears in the winning line.

The result of this is passed as a prop onto the Square component and from here, we simply style the square differently based on whether it is a winning square.

See the Pen React Tutorial - Q5 by Alvin Pascoe (@alvinpascoe) on CodePen.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

function Square(props){
  const winningSquareStyle = {
    backgroundColor: '#ccc'
  };

  return (
    <button className="square" onClick={props.onClick} style={props.winningSquare ? winningSquareStyle : null}>
      {props.value}
    </button>
  );
}

class Board extends React.Component {
  renderSquare(i) {
    let winningSquare = this.props.winner && this.props.winner.includes(i) ? true : false;
    return (
      <Square
       value={this.props.squares[i]}
       onClick={() => this.props.onClick(i)}
       winningSquare = {winningSquare}
      />
    );
  }

  render() {
    let boardSquares = [];
    for(let row = 0; row < 3; row++){
      let boardRow = [];
      for(let col = 0; col < 3; col++){
        boardRow.push(<span key={(row * 3) + col}>{this.renderSquare((row * 3) + col)}</span>);
      }
      boardSquares.push(<div className="board-row" key={row}>{boardRow}</div>);
    }

    return (
      <div>
        {boardSquares}
      </div>
    );
  }
}

class Game extends React.Component {
  constructor() {
    super();
    this.state = {
      history: [{
        squares: Array(9).fill(null),
        clickedSquare: [0,0],
      }],
      stepNumber: 0,
      xIsNext: true,
      ascending: true
    };
  }

  handleClick(i) {
    const history = this.state.history.slice(0, this.state.stepNumber + 1);
    const current = history[history.length - 1];
    const squares = current.squares.slice();
    if (calculateWinner(squares) || squares[i]) {
      return;
    }
    squares[i] = this.state.xIsNext ? 'X' : 'O';
    this.setState({
      history: history.concat([{
        squares: squares,
        clickedSquare:[Math.floor((i % 3) + 1), Math.floor((i / 3) + 1)]
      }]),
      stepNumber: history.length,
      xIsNext: !this.state.xIsNext,
    });
  }

  jumpTo(step) {
    this.setState({
      stepNumber: step,
      xIsNext: (step % 2) === 0,
    });
  }

  sortHandleClick(){
    this.setState({
      ascending: !this.state.ascending
    });
  }

  render() {
    const active = {
      fontWeight: 'bold'
    };

    const inactive = {
      fontWeight: 'normal'
    };

    const history = this.state.history;
    const current = history[this.state.stepNumber];
    const winner = calculateWinner(current.squares);
    const ascending = this.state.ascending;

    const moves = history.map((step, move) => {
      const clickedSquare = step.clickedSquare;
      const desc = move ?
        `Move #  ${move} - (${clickedSquare[0]},${clickedSquare[1]})`:
        `Game start`;

      return (
        <li key={move}>
          <a href="#" style={this.state.stepNumber === move ? active : inactive} onClick={() => this.jumpTo(move)}>{desc}</a>
        </li>
      );
    });

    let status;
    if (winner) {
      status = 'Winner: ' + winner.winner;
    } else {
      status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
    }


    return (
      <div className="game">
        <div className="game-board">
          <Board
            squares={current.squares}
            onClick={(i) => this.handleClick(i)}
            winner={winner && winner.winningSquares}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <ol>{ascending ? moves : moves.reverse()}</ol>
          <button onClick={() => this.sortHandleClick()}>Toggle Sort Order</button>
        </div>
      </div>
    );
  }
}

// ========================================

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


function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return {
        winner: squares[a],
        winningSquares: lines[i]
      };
    }
  }
  return null;
}
  • JavaScript
  • React
  • Tutorial
  • Previous Article: React Website Tutorial - Improvement 4
    Previous Article: React Website Tutorial - Improvement 4
  • Next Article: The Beauty of Code
    Next Article: The Beauty of Code

Blog Archives

  • December 2019 (1)
  • November 2019 (1)
  • August 2019 (1)
  • May 2019 (1)
  • October 2018 (1)
  • February 2017 (2)
  • January 2017 (3)
  • November 2016 (1)
  • July 2016 (1)
  • January 2016 (1)
More...

Categories

  • Angular
  • CSS
  • Development Environment
  • Drupal
  • Git
  • JavaScript
  • Mac
  • Programming
  • React
  • Tutorial
  • Workflow
  • Home
  • Blog
  • Sandbox
  • Pattern Library
© 2026 Alvin Pascoe
hello{at}alvinpascoe{dot}com
Made by Me, Alvin Pascoe