MENU

假装学会 React

2020 年 08 月 12 日 • 阅读: 1128 • 前端

浏览了 React 官网的 入门教程,完成结尾问题并记录答案。

预览

答案

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

首先为 Game 组件的第一个 history state 添加坐标属性 coordinate

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      history: [
        {
          squares: Array(9).fill(null),
          coordinate: [0, 0],
        },
      ],
    // ...
    };
  }
  // ...
}

修改 handleClick 函数,让 Board 每次被点击时,保存自己的坐标。

class Game extends React.Component {
  // ...
  handleClick(i) {
  //...
    this.setState({
      history: history.concat([
        {
          squares: squares,
          coordinate: [parseInt(i / 3) + 1, (i % 3) + 1],
        },
      ]),
    //...
    });
  }
  //...
}

最后,在 render 函数中,将当前坐标放到描述中。

  render() {
    const history = this.state.history;
    const moves = history.map((step, move) => {
      const desc = move
        ? "Go to move #" + move + ", coordinate: (" + step.coordinate + ")"
        : "Go to game start";
      // ...
    });
  // ...
}

在历史记录列表中加粗显示当前选择的项目

修改 Game 组件的 render 函数,如果 <button> 是当前步骤,通过 style 属性让其字体加粗。

class Game extends React.Component {
  // ...
  render() {
    const moves = history.map((step, move) => {
      return (
        <li key={move}>
          <button
            style={{
              fontWeight: move === this.state.stepNumber ? "bold" : "normal",
            }}
          >
          </button>
        </li>
      );
    });
}

使用两个循环来渲染出棋盘的格子,而不是在代码里写死(hardcode)

class Board extends React.Component {
  // ...
  render() {
    const seq = Array(3).fill(null);
    return (
      <div>
        {seq.map((_, i) => (
          <div key={i} className="board-row">
            {seq.map((_, j) => this.renderSquare(3 * i + j))}
          </div>
        ))}
      </div>
    );
  }
}

添加一个可以升序或降序显示历史记录的按钮

首先在 Game 组件的 state 中添加 升序 属性 ascSteps。接着改变 render 方法,在 status <div> 后增加一个按钮,内部文字跟随 ascSteps 作相应改变,再为其添加 onClick 属性,点击后调用 reverseMovesOrder 函数,反转 ascSteps

接着,在 <ol> 标签中添加 reversed 属性,根据 ascSteps 设置是与否。同时,其 children 也根据 ascSteps 决定是否反转。最后编写 reverseMovesOrder 函数,当它被调用时,改变状态中的 ascSteps

class Game extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      // ...
      ascSteps: true,
    };
  }

  reverseMovesOrder() {
    this.setState({
      ascSteps: !this.state.ascSteps,
    });
  }

  render() {
    // ...
    return (
      <div className="game">
        /* ... */
        <div className="game-info">
          <div>{status}</div>
          <button
            onClick={() => this.reverseMovesOrder()}
          >
            {(this.state.ascSteps ? "Descend" : "Ascend") + " step order"}
          </button>
          <ol reversed={this.state.ascSteps ? false : true}>
            {this.state.ascSteps ? moves : moves.reverse()}
          </ol>
        </div>
      </div>
    );
  }
  // ...
}

每当有人获胜时,高亮显示连成一线的 3 颗棋子

首先,改变 calculateWinnerLine 函数,当有人获胜时,返回整个获胜行。

function calculateWinnerLine(squares) {
  // ...
  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 lines[i];
    }
  }
  return null;
}

接着,在 Game 组件中,把获胜行传给 Board 组件。

class Game extends React.Component {
  // ...
  
  render() {
    // ...
    const winnerLine = calculateWinnerLine(current.squares);
    return (
      <div className="game">
        <div className="game-board">
          <Board
            winnerLine={winnerLine}
          />
        </div>
        /* ... */
      </div>
    );
  }
}

最后,改变 Board 组件的 renderSquare 方法,如果自身包含在获胜行中,则背景设为 yellow。同时,Square 组件要接收 style 属性,并将其设置到原生 <button> 上。

class Board extends React.Component {
  renderSquare(i) {
    let bgColor = "white";
    if (this.props.winnerLine && this.props.winnerLine.includes(i)) {
      bgColor = "yellow";
    }
    return (
      <Square
        style={{
          backgroundColor: bgColor,
        }}
        /* ... */
      />
    );
  }
  // ...
}

function Square(props) {
  return (
    <button style={props.style} /* ... */>
      {props.value}
    </button>
  );
}

当无人获胜时,显示一个平局的消息

改变 Game 组件的 render 函数,有赢家则显示,如果没有,接着检查方格是否被占满,如果是则为平局,否则显示下个 player

class Game extends React.Component {
    render() {
      const history = this.state.history;
      const current = history[this.state.stepNumber];
      const winnerLine = calculateWinnerLine(current.squares);
      let status;
      if (winnerLine) {
        status = "Winner: " + current.squares[winnerLine[0]];
      } else if (!current.squares.includes(null)) {
        status = "Game over, being a draw";
      } else {
        status = "Next player: " + (this.state.xIsNext ? "X" : "O");
      }
      // ...
    }
    // ...
}

相关

TG 大佬群 QQ 大佬群 QQ 追番群

返回文章列表 文章二维码
本页链接的二维码
打赏二维码
添加新评论

已有 3 条评论
  1. 小蚂蚁 小蚂蚁     Windows 7 /    Google Chrome

    这就是大佬的世界吧,几行代码就可以编出一个游戏

    1. LOGI LOGI     Windows 10 /    Google Chrome

      @小蚂蚁不是

  2. icelo icelo     Windows 10 /    Google Chrome

    大佬大佬@(花心)

0:00