Skip to main content
  • Home
  • Blog
  • Sandbox

Alvin Pascoe

Website Developer

Alvin Pascoe
Loading.....
  • React Website Tutorial - Improvement 2
    React Website Tutorial - Improvement 2
  • React Website Tutorial - Improvement 4
    React Website Tutorial - Improvement 4

React Website Tutorial - Improvement 3

Update: The file in this tutorial is now available on GitHub: Rewrite Board to use two loops to make the squares instead of hardcoding them.

25th January, 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.

Rewrite Board to use two loops to make the squares instead of hardcoding them.

We can rewrite the board using nested loops. We'll use one loop to create three individual squares and another one to take these squares and wrap them in a div. We'll do this last bit three times...giving us our 3x3 grid!

Since we'll be using a loop to create components, we need to add a key property so React can tell them apart. We'll need to add a key to both the individual squares and the rows. For the row, we can use the loop index as the key, the squares can use their unique grid number (0, 1, 2 etc). In order to add a key to individual squares, we'll wrap them in a span.

Remember, the key only has to be unique to sibling components. It's fine if some squares and row components share the same key.

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

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

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

class Board extends React.Component {
  renderSquare(i) {
    return (
      <Square
       value={this.props.squares[i]}
       onClick={() => this.props.onClick(i)}
      />
    );
  }

  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,
    };
  }

  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,
    });
  }

  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 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;
    } 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)}
          />
        </div>
        <div className="game-info">
          <div>{status}</div>
          <ol>{moves}</ol>
        </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 squares[a];
    }
  }
  return null;
}
  • JavaScript
  • React
  • Tutorial
  • Previous Article: React Website Tutorial - Improvement 2
    Previous Article: React Website Tutorial - Improvement 2
  • Next Article: React Website Tutorial - Improvement 4
    Next Article: React Website Tutorial - Improvement 4

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