Skip to main content
  • Home
  • Blog
  • Sandbox

Alvin Pascoe

Website Developer

Alvin Pascoe
Loading.....
  • React Website Tutorial - Improvement 3
    React Website Tutorial - Improvement 3
  • React Website Tutorial - Improvement 5
    React Website Tutorial - Improvement 5

React Website Tutorial - Improvement 4

Update: The file in this tutorial is now available on GitHub: Add a toggle button that lets you sort the moves in either ascending or descending order.

2nd 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.

Add a toggle button that lets you sort the moves in either ascending or descending order.

In order to toggle the move sort direction, we need to create a variable to track whether we're currently sorting in ascending or descending order. Adding another property to the Game component's state works well for this (I used ascending).

Next, we need to add a simple button to our output (responsible for toggling the sort order) and create an onClick method. The handler for the onClick method will simply inverse the state property used to track order.

Once sort order is being correctly tracked and updated, we can rewrite the code that displays the moves to check the ascending property and based on the result, display the moves in chronological or reverse order.

See the Pen React Tutorial - Q4 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,
      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;
    } 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>{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 squares[a];
    }
  }
  return null;
}
  • JavaScript
  • React
  • Tutorial
  • Previous Article: React Website Tutorial - Improvement 3
    Previous Article: React Website Tutorial - Improvement 3
  • Next Article: React Website Tutorial - Improvement 5
    Next Article: React Website Tutorial - Improvement 5

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