Some time ago for a foreign company, more free time.

Although I do Java backend, foreigners like agile development and full stack, so I also need to write the front end. Since I am a foreigner, I must like to use React. However, I have never written React before, so I have to start from the basics.

Tic tac toe

React tic-Tac-toe is the official React tutorial for beginners, here.

The rules of the game are as follows:

  • A total of 9 checkerboard, a total of 2 pieces, respectively X and O
  • Each time, a player drops a piece onto a grid
  • If three of the same pieces are connected on a horizontal/vertical/diagonal line, the game is won, otherwise it is a draw

The end result looks like this:

implementation

My implementation is completely follow the official tutorial, finally selected some interesting homework to complete, respectively added the following functions:

  • When you click on an item in the history list, the text for that item is bold
  • The original hard – coded nine – grid into a cycle
  • If it’s a tie, show it on the screen

They are all relatively simple functions. I also thought of a function, which is to give a replay button, and click it to replay the whole game process.

So, by adding the implementation of the above four functions, the end result becomes:

When you click on an item in the history list, the text for that item is bold

Add a field selectedIndex to state to indicate which history item is being selected. This field will be used during rendering.

    this.state = {
      history: [
        {
          squares: Array(9).fill(null),
        },
      ],
      currentStep: 0,
      selectedIndex: -1,
      xIsNext: true
    };
Copy the code

When rendering, the code looks like this:

    const moves = history.map((item, index) => {
      const desc = index > 0 ? `Go to move ${index}` : `Go to game start`
      const textHtml = this.state.selectedIndex === index ? (<b>{desc}</b>) : desc
      return (
        <li key={index}>
          <button onClick={() => this.jumpTo(index)}>{textHtml}</button>
        </li>
      )
    })
Copy the code

As the entire history list is generated, each item is iterated through to determine whether the subscripts of the selected items stored in the state are equal to the subscripts of an item. If they are equal, a bold label is placed outside the text.

When you click on an item, you need to set the value of selectedIndex, and the code looks like this:

  jumpTo(i) {
    this.setState({
      currentStep: i,
      selectedIndex: i,
      xIsNext: (i & 1) === 0
    });
  }
Copy the code

The original hard – coded nine – grid into a cycle

This is a bit of a snag. I wanted to use a double loop, like this:

    let board = []
    for (let i = 0; i < 3; ++i) {
      board.push(<div className="board-row">)
      for (let j = 0; j < 3; ++j) {
        board.push((j) => {this.renderSquare(i * 3 + j)})
      }
      board.push(</div>)
    }
    return (
      <div>
        {board}
      </div>
    );
Copy the code

But in this line:

board.push(<div className="board-row">)
Copy the code

It thinks THAT I did not close the tag, and the following code is still content, so it will report an error, so FINALLY I can only use map to write, double loop should be ok, just started, not familiar with.

The final code implemented with Map looks like this:

    let board = []
    for (let i = 0; i < 3; ++i) {
      board.push(
        <div className="board-row">
        {[0, 1, 2].map((j) => this.renderSquare(i * 3 + j))}
        </div>
      )
    }
    return (
      <div>
        {board}
      </div>
    );
Copy the code

If it’s a tie, show it on the screen

If no winner is counted and all the pieces are filled up on the board, it is considered a draw. The code is as follows:

    if (winner) {
      status = `Winner: ${winner}`}else if (history.length > 9) {
      status = `No Winner, it's draw` }Copy the code

playback

The implementation is also relatively simple, the idea isto use history to save the state, use setInterval to reset, when traversing the last state with clearInterval stop.

First, add a button:

        <div className="game-info">
          <div>{status}</div>
          <ol>{moves}</ol>
          <div><button onClick={() => this.playBack()}>Play back</button></div>
        </div>
Copy the code

Then look at the logic this button invokes:

  playBack() {
    this.setState({
      currentStep: 0,
      selectedIndex: 0,
      xIsNext: true
    });
    var intervalID = setInterval(() => {
      this.setState({
        currentStep: this.state.currentStep + 1,
        selectedIndex: this.state.selectedIndex + 1,
        xIsNext: (this.state.currentStep & 1) === 0
      });     
      if (this.state.currentStep >= this.state.history.length - 1) {
        clearInterval(intervalID);
        this.setState({
          xIsNext: (this.state.currentStep & 1) === 0
        })
      }
    }, 1000)
  }
Copy the code

That’s the whole implementation! I forgot to attach the source code here

This article first appeared on my blog.