The document concept is in a daze, turn around and forget, or their own knock, follow the official document to a chessboard practice.

import React from 'react';
import ReactDOM, { createPortal } from 'react-dom';
import './index.css';
// 5.9: State promotion (child components no longer retain state separately, but are promoted to common parent components for unified maintenance and modification)
// 5.9: Function components (no state, only render function, simpler than class components) do not need to render, directly return; No this, direct props; No life cycle
// 5.9: Both sides take turns to drop, define Boolean variables to control switching and update booleans
If (XXX) return if(XXX) return if(XXX) return
// 5.9: Determine the winner and iterate through the successful array
// 5.10: State promotion board saves state promotion to game component stepNumber control step
// 5.10: Time travel
// 5.12: The game history list shows the coordinates of each move in the format (column number, row number). Programmers count from 0! Column number = index % Total column number =parseInt(index/total column number)
// 5.13: Added a button that displays history in ascending or descending order. Added a Boolean binding to the ol TAB's reverse attribute list Note that reverse changes the original array directly
// 5:13: Whenever someone wins, highlight the 3 pieces in a line. Props to pass winnerline
// 5.13: Display a draw message when no one wins, stepNumber is 9 when clicked
// // Square class component
// class Square extends React.Component {
// render () {
// // console.log(this) // Square instance
// return (
// 
// {this.props.value}
// 
/ /);
/ /}
// }
// Square functional component
function Square (props) {
  return (
    <button className={`squareThe ${props.isWinner ? 'winner ' :"'} `}onClick={props.onClick}>
      {props.value}
    </button>)}class Board extends React.Component {
  renderSquare (i) {
    return<Square key={i} value={this.props.squareArr[i]} isWinner={this.props.winnerLine && this.props.winnerLine.includes(i)} onClick={() => this.props.onClick(i)} />; } render () { const finalArr = [ [0, 1, 2], [3, 4, 5], [6, 7, 8] ] return ( <div> { finalArr.map((outter, index) => { return ( <div key={index} className="board-row"> { outter.map(item => { return this.renderSquare(item) }) } </div> ) }) } </div> ); } } class Game extends React.Component { constructor(props) { super(props) this.state = { history: [ { squareArr: Array(9).fill(null), currentXY: null, step: 0 } ], XIsNext: true, stepNumber: 0, isReversed: false } } render () { let history = this.state.history const current = history[this.state.stepNumber] // debugger const { winner, winnerLine } = this.caculateWinner(current.squareArr) const status = winner ? 'Winner' + winner : 'Next player: ' + (this.state.XIsNext ? 'X' : 'O') const tempHistory = history.slice() if (this.state.isReversed) { tempHistory.reverse() } const moves = tempHistory.map((item, index) => { var desc = item.currentXY || item.currentXY === 0 ? `Go To Move #${item.step} Col: ${parseInt(item.currentXY % 3)} Row: ${parseInt(item.currentXY / 3)}` : 'Go To game start' return ( <li key={item.currentXY}> <button onClick={() => this.jumpTo(item.step)} className={item.step === this.state.stepNumber ? 'active' : Null} >{desc}</button></li>)}) return (<div className="game"> <div className="game-board"> <Board squareArr={current.squareArr} winnerLine={winnerLine} onClick={(i) => this.handleClick(i)} /> </div> <div className="game-info"> <button onClick={() => { this.handleToggleSort() }}>{this.state.isReversed ? 'ascending' : 'descending'} < / button > < div > {status} < / div > < ol reversed = {this. State. IsReversed} > {moves} < / ol > < / div > < / div >). } handleClick (i) { const history = this.state.history.slice(0, this.state.stepNumber + 1) const current = history[history.length - 1] let squareArr = current.squareArr.slice() if (this.caculateWinner(squareArr).winner || squareArr[i] && this.state.stepNumber < 9) { return } squareArr[i] = this.state.XIsNext ? 'X' : 'O' this.setState({ history: history.concat({ squareArr: squareArr, currentXY: i, step: this.state.stepNumber + 1 }), XIsNext: ! this.state.XIsNext, stepNumber: history.length }, () => { setTimeout(() => { if (! This.caculatewinner (squareArr).winner && this.state.stepNumber >= 9 {console.log(this.state.stepNumber) alert(' tie ')} }, 0)}) // TODO: This will pop up a dialog box and then update the page is not the desired result should first update the page and then pop up a dialog box is there any alternative? // this.setState({ // history: history.concat({ squareArr: squareArr, currentXY: i, step: this.state.stepNumber + 1 }), // XIsNext: ! this.state.XIsNext, // stepNumber: history.length // }, () => { // if (! this.caculateWinner(squareArr).winner && this.state.stepNumber >= 9) { // console.log(this.state.stepNumber) // Alert (' draw ') / / / /}})} caculateWinner (squareArr) {let winnerArr = [[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 < winnerArr.length; i++) { let [a, b, c] = winnerArr[i] if (squareArr[a] && squareArr[a] === squareArr[b] && squareArr[a] === squareArr[c]) { return { winner:  squareArr[a], winnerLine: winnerArr[i] } } } return { winner: null } } jumpTo (index) { let history = this.state.history this.setState({ stepNumber: index, XIsNext: index % 2 === 0 ? 'O' : })} handleToggleSort () {this.setState({this.setState({this.setState({this.setState(})} isReversed: ! this.state.isReversed }) } } // ======================================== ReactDOM.render( <Game />, document.getElementById('root') );Copy the code