MENU

假装学会 React

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

浏览了 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 大佬群

最后编辑于: 2020 年 12 月 03 日
返回文章列表 文章二维码
本页链接的二维码
打赏二维码
添加新评论

Loading captcha...

  • OωO
  • |´・ω・)ノ
  • ヾ(≧∇≦*)ゝ
  • (☆ω☆)
  • (╯‵□′)╯︵┴─┴
  •  ̄﹃ ̄
  • (/ω\)
  • ∠( ᐛ 」∠)_
  • (๑•̀ㅁ•́ฅ)
  • →_→
  • ୧(๑•̀⌄•́๑)૭
  • ٩(ˊᗜˋ*)و
  • (ノ°ο°)ノ
  • (´இ皿இ`)
  • ⌇●﹏●⌇
  • (ฅ´ω`ฅ)
  • (╯°A°)╯︵○○○
  • φ( ̄∇ ̄o)
  • ヾ(´・ ・`。)ノ"
  • ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
  • (ó﹏ò。)
  • Σ(っ °Д °;)っ
  • ( ,,´・ω・)ノ"(´っω・`。)
  • ╮(╯▽╰)╭
  • o(*////▽////*)q
  • >﹏<
  • ( ๑´•ω•) "(ㆆᴗㆆ)
  • (。•ˇ‸ˇ•。)
  • 泡泡
  • 阿鲁
  • 蛆音娘
  • 小埋
  • 颜文字

已有 5 条评论
  1. 强调几点:(该留言由系统自动生成!)
    1. 请不要刷广告,本站没有流量!
    2. 我不回复虚假邮箱,因为回复了你也看不到!
    3. 存在必须回复的隐藏内容时,可以直接使用表情框里的阿鲁表情!

  1. PAN PAN   Mac OS X 10.14.6  Google Chrome 85.0.4183.102

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

    1. clearlove clearlove   Mac OS X 10.15.7  Google Chrome 90.0.4430.85

      @PAN 这是 github 上的一个开源代码分享的,入门 react 的

  2. 小蚂蚁 小蚂蚁   Windows 7 x64 Edition  Google Chrome 81.0.4044.92

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

    1. LOGI LOGI   Windows 10 x64 Edition  Google Chrome 85.0.4183.83

      @小蚂蚁不是

  3. icelo icelo   Windows 10 x64 Edition  Google Chrome 84.0.4147.125

    大佬大佬