React学习——井字游戏(官网教程笔记)

一、安装node

  1. 可以通过官网下载稳定版本并执行安装;
  2. 或者通过nvm安装你需要的node版本(可以对node版本进行管理,比较推荐)。

二、创建新项目

  1. 打开命令终端;

  2. 进入你要存放项目的路径;

  3. 创建react项目"tic-tac-toe"(项目名称):

    npx create-react-app tic-tac-toe
    
  4. 进入项目"tic-tac-toe":

    cd tic-tac-toe
    
  5. 运行项目:

    run start
    
  6. 浏览器访问地址http://localhost:3000,可看到默认页面;

  7. 使用IDE或者编辑器打开项目(推荐使用 webstorm、vscode ),列表如下,已忽略"node_modules":

    │  .gitignore
    │  package-lock.json
    │  package.json
    │  README.md
    │          
    ├─public
    │      favicon.ico
    │      index.html
    │      logo192.png
    │      logo512.png
    │      manifest.json
    │      robots.txt
    │      
    └─src
            App.css
            App.js
            App.test.js
            index.css
            index.js
            logo.svg
            serviceWorker.js
            setupTests.js
    
  1. 删除src目录下的所有文件;

三、初始化项目文件

  1. 在src目录下创建index.css,可以自己编写css样式代码,也可以将例子中的样式代码复制到此文件中;

    body {
      font: 14px "Century Gothic", Futura, sans-serif;
      margin: 20px;
    }
    
    ol, ul {
      padding-left: 30px;
    }
    
    li {
      list-style: none;
    }
    
    .board-row:after {
      clear: both;
      content: "";
      display: table;
    }
    
    .status {
      margin-bottom: 10px;
    }
    
    .square {
      background: #fff;
      border: 1px solid #999;
      float: left;
      font-size: 24px;
      font-weight: bold;
      height: 60px;
      margin-right: -1px;
      margin-top: -1px;
      padding: 0;
      text-align: center;
      width: 60px;
    }
    
    .square:focus {
      outline: none;
    }
    
    .kbd-navigation .square:focus {
      background: #ddd;
    }
    
    .game {
      display: flex;
      flex-direction: row;
      justify-content: center;
    }
    
    .game-info {
      margin-left: 20px;
    }
    
  2. 在src目录下创建index.js,引入所需要的依赖:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    
  3. 将例子中的javascript代码复制到index.js文件中;

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    
    class Square extends React.Component {
      render() {
        return (
          
        );
      }
    }
    
    class Board extends React.Component {
      renderSquare(i) {
        return ;
      }
    
      render() {
        const status = 'Next player: X';
    
        return (
          
    {status}
    {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)}
    {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)}
    {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)}
    ); } } class Game extends React.Component { render() { return (
    {/* status */}
      {/* TODO */}
    ); } } // ======================================== ReactDOM.render( , document.getElementById('root') );
  4. 再次查看浏览器,此时应该已经刷新页面,或者可以手动刷新;

四、改造代码

  1. Board组件中向Square组件传递参数,value={i}

      renderSquare(i) {
        return ;
      }
    
  2. Square组件中接收Board组件传来的参数,并显示:

    class Square extends React.Component {
      render() {
        return (
          
        );
      }
    }
    
  3. 尝试在Square组件上添加点击方法:

    class Square extends React.Component {
      render() {
        return (
          
        );
      }
    }
    
  4. Square组件中添加状态用来记录按钮被点击过:

    class Square extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          value: null
        };
      }
      render() {
        return (
          
        );
      }
    }
    
  5. 通过setState()方法,当点击按钮,就把按钮的显示内容改为state.value的值:

    class Square extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          value: null
        };
      }
      render() {
        return (
          
        );
      }
    }
    

五、进一步完善,轮流落子

  1. Square组件中的状态统一移动到父组件Board中进行管理:

    class Board extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          squares: Array(9).fill(null)
        }
      }
    }
    
  2. 在父组件Board中向子组件Square传递两个参数valueonClick

    class Board extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          squares: Array(9).fill(null)
        }
      }
      renderSquare(i) {
        return  this.handleClick(i)} />;
      }
    }
    
  3. 在子组件Square修改按钮显示为父组件传来的参数,监听的点击方法也为父组件传来的方法:

    class Square extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          value: null
        };
      }
      render() {
        return (
          
        );
      }
    }
    
  4. 删除子组件Square的自有状态:

    class Square extends React.Component {
      render() {
        return (
          
        );
      }
    }
    
  5. 优化子组件Square为函数组件,无状态使用函数组件更效率:

    function Square(props) {
      return (
        
      );
    }
    
  6. 定义父组件Board的handleClick方法:

      handleClick(i) {
        let temp = this.state.squares.slice();
        temp[i] = "X";
        this.setState({
          squares: temp
        });
      }
    
  7. 完善轮流落子功能,在父组件Board中设置一个开关isX,默认为true,每落下一步,isX将反转,依此判断下一步该落什么子:

     constructor(props) {
       super(props);
       this.state = {
         squares: Array(9).fill(null),
         isX: true
       }
     }  
     handleClick(i) {
       let temp = this.state.squares.slice();
       temp[i] = this.state.isX ? "X" : "O";
       this.setState({
         squares: temp,
         isX: !this.state.isX
       });
     }
    
  8. 在父组件Board中在修改提示信息,以便知道下一步是什么子:

    const status = `Next player: ${this.state.isX ? "X" : "O"}`;
    

六、进一步完善,判断输赢

  1. 编写判断输赢的方法,放在index.js最后面,写好输赢规则lines,依次遍历参数lines,将squares中每一项进行三三比对,得出结果:

    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;
    }
    
  2. 父组件Board中,修改点击事件,当已经有人胜出,或者当前方格已经有数据,不做操作:

      handleClick(i) {
        let temp = this.state.squares.slice();
        if (calculateWinner(this.state.squares) || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          squares: temp,
          isX: !this.state.isX
        });
      }
    

七、继续完善,增加历史记录功能

  1. 在顶层Game组件中添加状态history,用来保存每一步的棋盘数据,并且把Board组件中的状态移到Game组件中:

    class Game extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          history: [{
            squares: Array(9).fill(null)
          }],
          isX: true
        }
      }
    }
    
  2. Board组件中的状态移除,并把原先状态中的参数改为接收父组件Game传来的参数:

    class Board extends React.Component {
      renderSquare(i) {
        return  this.props.onClick(i)} />;
      }
      render() {
        return (
          
    {this.props.status}
    {this.renderSquare(0)} {this.renderSquare(1)} {this.renderSquare(2)}
    {this.renderSquare(3)} {this.renderSquare(4)} {this.renderSquare(5)}
    {this.renderSquare(6)} {this.renderSquare(7)} {this.renderSquare(8)}
    ); } }
  3. Board组件中的handleClick方法移动到父组件Game中,根据当前Game组件的状态进行调整:

    class Game extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          history: [{
            squares: Array(9).fill(null)
          }],
          isX: true
        }
      }
      handleClick(i) {
        const history = this.state.history;
        const cur = history[history.length -1];
        let temp = cur.squares.slice();
        if (calculateWinner(temp) || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          history: history.concat([{
            squares: temp
          }]),
          isX: !this.state.isX
        });
      }
    }
    
  4. Board组件中render方法中的前置判断移动到父组件Game中,并在调用Board组件的时候传入参数squaresstatusonClick

    class Game extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          history: [{
            squares: Array(9).fill(null)
          }],
          isX: true
        }
      }
      handleClick(i) {
        const history = this.state.history;
        const cur = history[history.length -1]; // 使用最新一次的棋盘数据
        let temp = cur.squares.slice();
        if (calculateWinner(temp) || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          history: history.concat([{
            squares: temp
          }]),
          isX: !this.state.isX
        });
      }
      render() {
        const history = this.state.history;
        const cur = history[history.length -1]; // 使用最新一次的棋盘数据
        const winner = calculateWinner(cur.squares);
        let status = "";
        if (winner) {
          status = `Winner is: ${winner}`;
        } else {
          status = `Next player: ${this.state.isX ? "X" : "O"}`;
        }
    
        return (
          
    this.handleClick(i)} />
    {/* status */}
      {/* TODO */}
    ); } }

八、展示历史记录

  1. Game中渲染一个历史记录按钮列表,使用history状态的map方法形成一个history的历史记录映射:

        const moves = history.map((item, i) => {
          const con = i ? `Go to step-${i}` : `Go to start`;
          return(
            
  2. ) });
  3. 给每一步添加唯一key:

        const moves = history.map((item, i) => {
          const con = i ? `Go to step-${i}` : `Go to start`;
          return(
            
  4. ) });
  5. Game中将moves映射插入到对应展示的位置:

        return (
          
    this.handleClick(i)} />
    {status}
      {moves}
    )
  6. Game中声明状态参数step,来表示当前步骤:

      constructor(props) {
        super(props);
        this.state = {
          history: [{
            squares: Array(9).fill(null)
          }],
          isX: true,
          step: 0
        }
      }
    
  7. Game中编写jumpTo方法,更改step状态,并且当步数是偶数时,isXtrue

      jumpTo(i) {
        this.setState({
          step: i,
          isX: i % 2 === 0
        })
      }
    
  8. Game中修改handleClick方法,每走一步,同时更新step状态:

      handleClick(i) {
        const history = this.state.history;
        const cur = history[history.length -1];
        let temp = cur.squares.slice();
        if (calculateWinner(temp) || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          history: history.concat([{
            squares: temp
          }]),
          isX: !this.state.isX,
          step: history.length
        });
      }
    
  9. Game中修改handleClick方法,将history改成截取0到当前步骤的数据:

      handleClick(i) {
        const history = this.state.history.slice(0, this.state.step + 1);
        const cur = history[history.length -1];
        let temp = cur.squares.slice();
        if (calculateWinner(temp) || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          history: history.concat([{
            squares: temp
          }]),
          isX: !this.state.isX,
          step: history.length
        });
      }
    
  10. Game中修改render方法中当前棋盘通过当前步骤获取:

        const history = this.state.history;
        const cur = history[this.state.step]; //通过当前步骤获取 
        const winner = calculateWinner(cur.squares);
    
  11. 完成

九、功能优化

  1. 在游戏历史记录列表显示每一步棋的坐标,格式为 (列号, 行号):

    class Board extends React.Component {
      renderSquare(i, axis) {
        /* 接收坐标参数 */
        return  this.props.onClick(i, axis)} />; 
      }
      render() {
        return (
          
    {this.props.status}
    {this.renderSquare(0, [1,1])} // 传入坐标参数 {this.renderSquare(1, [1,2])} {this.renderSquare(2, [1,3])}
    {this.renderSquare(3, [2,1])} {this.renderSquare(4, [2,2])} {this.renderSquare(5, [2,3])}
    {this.renderSquare(6, [3,1])} {this.renderSquare(7, [3,2])} {this.renderSquare(8, [3,3])}
    ); } }
    class Game extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          history: [{
            squares: Array(9).fill(null),
            axis: [] // 声明坐标状态
          }],
          isX: true,
          step: 0
        }
      }
    
      handleClick(i, axis) { // 接收坐标参数
        const history = this.state.history.slice(0, this.state.step + 1);
        const cur = history[history.length - 1];
        let temp = cur.squares.slice();
        if (calculateWinner(temp) || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          history: history.concat([{
            squares: temp,
            axis: axis // 更改坐标状态
          }]),
          isX: !this.state.isX,
          step: history.length,
        });
      }
    
      jumpTo(i) {
        this.setState({
          step: i,
          isX: (i % 2) === 0
        });
      }
    
      render() {
        const history = this.state.history;
        const cur = history[this.state.step];
        const winner = calculateWinner(cur.squares);
    
        const moves = history.map((item, i) => {
          // 展示坐标
          const con = i ? `Go to step-${i}-[${item.axis[0]},${item.axis[1]}]` : `Go to Game start`;
          const randomKey = `moves${Math.floor(Math.random() * 900000 + 100000)}`;
          return(
            
  2. ) }); let status = ""; if (winner) { status = `Winner is: ${winner}`; } else { status = `Next player: ${this.state.isX ? "X" : "O"}`; } return (
    this.handleClick(i,axis)} // 接收、传入坐标参数 />
    {status}
      {moves}
    ); } }
  3. 在历史记录列表中加粗显示当前选择的项目:

          return(
            // 添加判断是否需要".active"
            
  4. )
    /* index.css 添加.active */
    .active {
      font-weight: bold;
      color: #38ccff;
    }
    
  5. 使用两个循环来渲染出棋盘的格子,而不是在代码里写死(hardcode):

    class Board extends React.Component {
      renderSquare(i, axis) {
        const randomKey = `cell${Math.floor(Math.random() * 900000 + 100000)}`;
        return  this.props.onClick(i, axis)} />;
      }
      // 渲染行方法
      renderRow(j) {
        const rowACell = this.props.rowACell;
        let temp = [];
        for (let i = 0; i < rowACell; i++) {
          temp.push(this.renderSquare(i+rowACell*j, [j+1,i+1]));
        }
        const randomKey = `row${Math.floor(Math.random() * 900000 + 100000)}`;
        return(
          
    {temp}
    ) } // 渲染棋盘方法 renderBoard() { const rowACell = this.props.rowACell; let temp = []; for (let i = 0; i < rowACell; i++) { temp.push(this.renderRow(i)); } return temp; } render() { return (
    {this.props.status}
    {this.renderBoard()} // 渲染棋盘
    ); } }
    class Game extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          history: [{
            squares: Array(9).fill(null),
            axis: []
          }],
          isX: true,
          step: 0,
          rowACell: 3 // 3*3棋盘,如需可变,需要修改相应的calculateWinner()方法规则
        }
      }
    
      handleClick(i, axis) {
        const history = this.state.history.slice(0, this.state.step + 1);
        const cur = history[history.length - 1];
        let temp = cur.squares.slice();
        if (calculateWinner(temp) || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          history: history.concat([{
            squares: temp,
            axis: axis
          }]),
          isX: !this.state.isX,
          step: history.length,
        });
      }
    
      jumpTo(i) {
        this.setState({
          step: i,
          isX: (i % 2) === 0
        });
      }
    
      render() {
        const history = this.state.history;
        const cur = history[this.state.step];
        const winner = calculateWinner(cur.squares);
    
        const moves = history.map((item, i) => {
          const con = i ? `Go to step-${i}-[${item.axis[0]},${item.axis[1]}]` : `Go to Game start`;
          const randomKey = `moves${Math.floor(Math.random() * 900000 + 100000)}`;
          return(
            
  6. ) }); let status = ""; if (winner) { status = `Winner is: ${winner}`; } else { status = `Next player: ${this.state.isX ? "X" : "O"}`; } return (
    this.handleClick(i,axis)} />
    {status}
      {moves}
    ); } }
  7. 添加一个可以升序或降序显示历史记录的按钮:

    class Game extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          history: [{
            squares: Array(9).fill(null),
            axis: []
          }],
          isX: true,
          step: 0,
          rowACell: 3, // 3*3棋盘,如需可变,需要修改相应的calculateWinner()方法规则
          order: true // 升序true,降序false
        }
      }
    
      handleClick(i, axis) {
        const history = this.state.history.slice(0, this.state.step + 1);
        const cur = history[history.length - 1];
        let temp = cur.squares.slice();
        if (calculateWinner(temp) || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          history: history.concat([{
            squares: temp,
            axis: axis
          }]),
          isX: !this.state.isX,
          step: history.length
        });
      }
    
      jumpTo(i) {
        this.setState({
          step: i,
          isX: (i % 2) === 0
        });
      }
      
      // 改变排序方法
      changeOrder() {
        this.setState({
          order: !this.state.order
        })
      }
      
      // 最终展示的历史记录
      historyCoder(moves) {
        if (this.state.order) {
          return moves;
        } else {
          return moves.reverse();
        }
      }
    
      render() {
        const history = this.state.history;
        const cur = history[this.state.step];
        const winner = calculateWinner(cur.squares);
    
        const moves = history.map((item, i) => {
          const con = i ? `Go to step-${i}-[${item.axis[0]},${item.axis[1]}]` : `Go to Game start`;
          const randomKey = `moves${Math.floor(Math.random() * 900000 + 100000)}`;
          return(
            
  8. ) }); let status = ""; if (winner) { status = `Winner is: ${winner}`; } else { status = `Next player: ${this.state.isX ? "X" : "O"}`; } return (
    this.handleClick(i,axis)} />
    // 改变按钮文字
    // 展示历史记录
      {this.historyCoder(moves)}
    ); } }
  9. 每当有人获胜时,高亮显示连成一线的 3 颗棋子:

    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 {
            player: squares[a],
            line: lines[i]
          };
        }
      }
      // 返回值改造
      return {
        player: null,
        line: []
      };
    }
    
    class Game extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          history: [{
            squares: Array(9).fill(null),
            axis: []
          }],
          isX: true,
          step: 0,
          rowACell: 3, // 3*3棋盘,如需可变,需要修改相应的calculateWinner()方法规则
          order: true, // 升序true,降序false
        }
      }
    
      handleClick(i, axis) {
        const history = this.state.history.slice(0, this.state.step + 1);
        const cur = history[history.length - 1];
        let temp = cur.squares.slice();
        // 修改winner
        const res = calculateWinner(temp);
        const winner = res.player;
        if (winner || temp[i]) {
          return;
        }
        temp[i] = this.state.isX ? "X" : "O";
        this.setState({
          history: history.concat([{
            squares: temp,
            axis: axis
          }]),
          isX: !this.state.isX,
          step: history.length
        });
      }
    
      jumpTo(i) {
        this.setState({
          step: i,
          isX: (i % 2) === 0
        });
      }
    
      changeOrder() {
        this.setState({
          order: !this.state.order
        })
      }
    
      historyCoder(moves) {
        if (this.state.order) {
          return moves;
        } else {
          return moves.reverse();
        }
      }
    
      render() {
        const history = this.state.history;
        const cur = history[this.state.step];
        // 修改winner
        const res = calculateWinner(cur.squares);
        const winner = res.player;
    
        const moves = history.map((item, i) => {
          const con = i ? `Go to step-${i}-[${item.axis[0]},${item.axis[1]}]` : `Go to Game start`;
          const randomKey = `moves${Math.floor(Math.random() * 900000 + 100000)}`;
          return(
            
  10. ) }); let status = ""; if (winner) { status = `Winner is: ${winner}`; } else { status = `Next player: ${this.state.isX ? "X" : "O"}`; } return (
    this.handleClick(i,axis)} />
      {this.historyCoder(moves)}
    ); } }
    class Board extends React.Component {
      renderSquare(i, axis) {
        // 判断是否高亮
        const highLight = this.props.highLight;
        const isHigh = highLight.includes(i);
        const randomKey = `cell${Math.floor(Math.random() * 900000 + 100000)}`;
        return  this.props.onClick(i, axis)} isHigh={isHigh} />; // 传入高亮
      }
    
      renderRow(j) {
        const rowACell = this.props.rowACell;
        let temp = [];
        for (let i = 0; i < rowACell; i++) {
          temp.push(this.renderSquare(i+rowACell*j, [j+1,i+1]));
        }
        const randomKey = `row${Math.floor(Math.random() * 900000 + 100000)}`;
        return(
          
    {temp}
    ) } renderBoard() { const rowACell = this.props.rowACell; let temp = []; for (let i = 0; i < rowACell; i++) { temp.push(this.renderRow(i)); } return temp; } render() { return (
    {this.props.status}
    {this.renderBoard()}
    ); } }
    function Square(props) {
      return (
        
      );
    }
    
  11. 当无人获胜时,显示一个平局的消息:

        // 修改Game组件render中的结果判断
        let status = "";
        let full = true;
        cur.squares.forEach(item => {
          full = full && item;
        });
        if (winner) {
          status = `Winner is: ${winner}`;
        } else if (!winner && full) {
          status = `It's a draw!!!`;
        } else {
          status = `Next player: ${this.state.isX ? "X" : "O"}`;
        }
    

十、完成啦,代码如下:

index.js

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

function Square(props) {
  return (
    
  );
}

class Board extends React.Component {
  renderSquare(i, axis) {
    const highLight = this.props.highLight;
    const isHigh = highLight.includes(i);
    const randomKey = `cell${Math.floor(Math.random() * 900000 + 100000)}`;
    return  this.props.onClick(i, axis)} isHigh={isHigh} />;
  }

  renderRow(j) {
    const rowACell = this.props.rowACell;
    let temp = [];
    for (let i = 0; i < rowACell; i++) {
      temp.push(this.renderSquare(i+rowACell*j, [j+1,i+1]));
    }
    const randomKey = `row${Math.floor(Math.random() * 900000 + 100000)}`;
    return(
      
{temp}
) } renderBoard() { const rowACell = this.props.rowACell; let temp = []; for (let i = 0; i < rowACell; i++) { temp.push(this.renderRow(i)); } return temp; } render() { return (
{this.props.status}
{this.renderBoard()}
); } } class Game extends React.Component { constructor(props) { super(props); this.state = { history: [{ squares: Array(9).fill(null), axis: [] }], isX: true, step: 0, rowACell: 3, // 3*3棋盘,如需可变,需要修改相应的calculateWinner()方法规则 order: true, // 升序true,降序false } } handleClick(i, axis) { const history = this.state.history.slice(0, this.state.step + 1); const cur = history[history.length - 1]; let temp = cur.squares.slice(); const res = calculateWinner(temp); const winner = res.player; if (winner || temp[i]) { return; } temp[i] = this.state.isX ? "X" : "O"; this.setState({ history: history.concat([{ squares: temp, axis: axis }]), isX: !this.state.isX, step: history.length }); } jumpTo(i) { this.setState({ step: i, isX: (i % 2) === 0 }); } changeOrder() { this.setState({ order: !this.state.order }) } historyCoder(moves) { if (this.state.order) { return moves; } else { return moves.reverse(); } } render() { const history = this.state.history; const cur = history[this.state.step]; const res = calculateWinner(cur.squares); const winner = res.player; const moves = history.map((item, i) => { const con = i ? `Go to step-${i}-[${item.axis[0]},${item.axis[1]}]` : `Go to Game start`; const randomKey = `moves${Math.floor(Math.random() * 900000 + 100000)}`; return(
  • ) }); let status = ""; let full = true; cur.squares.forEach(item => { full = full && item; }); if (winner) { status = `Winner is: ${winner}`; } else if (!winner && full) { status = `It's a draw!!!`; } else { status = `Next player: ${this.state.isX ? "X" : "O"}`; } return (
    this.handleClick(i,axis)} />
      {this.historyCoder(moves)}
    ); } } // ======================================== ReactDOM.render( , 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 { player: squares[a], line: lines[i] }; } } return { player: null, line: [] }; }

    index.css

    body {
      font: 14px "Century Gothic", Futura, sans-serif;
      margin: 20px;
    }
    
    ol, ul {
      padding-left: 30px;
    }
    
    li {
      list-style: none;
    }
    
    .board-row:after {
      clear: both;
      content: "";
      display: table;
    }
    
    .status {
      margin-bottom: 10px;
    }
    
    .square {
      background: #fff;
      border: 1px solid #999;
      float: left;
      font-size: 24px;
      font-weight: bold;
      height: 60px;
      margin-right: -1px;
      margin-top: -1px;
      padding: 0;
      text-align: center;
      width: 60px;
    }
    
    .square:focus {
      outline: none;
    }
    
    .kbd-navigation .square:focus {
      background: #ddd;
    }
    
    .game {
      display: flex;
      flex-direction: row;
      justify-content: center;
    }
    
    .game-info {
      margin-left: 20px;
    }
    
    .active {
      font-weight: bold;
      color: #38ccff;
    }
    
    

    你可能感兴趣的:(React学习——井字游戏(官网教程笔记))