React入門-井字遊戲實現與完善

前段時間換了家外企工做, 空閒時間比較多。html

雖然我是作Java後端的,可是老外喜歡搞敏捷開發和全棧,因此也要寫前端,既然是老外,那確定是喜歡用React的,然而我以前歷來沒寫過React,只能從基礎一步一步來了。前端

井字遊戲

井字遊戲是React官方的入門實戰教程的案例來的,在這裏react

這個遊戲的規則是這樣的:git

  • 一共9個格子的棋盤, 共2種棋子,分別是X和O
  • 每次,一個玩家將棋子下到一個格子上
  • 若是有3個相同棋子在橫/豎/對角線上相連,那就贏了,不然就爲和局

最後的效果是這樣的:github

實現

個人實現就是徹底跟着官方教程來的,最後挑了一些有趣的課後做業完成,分別新增了下面的功能:後端

  • 點擊歷史列表某一項時,這一項的文字加粗
  • 將原來硬編碼寫的九宮格改爲用循環
  • 若是是平局,在界面上顯示出來

都是比較簡單的功能,我還想到了一個功能,就是給一個回放的按鈕,點擊能夠回放整個遊戲過程。bash

因此,增長了上面的4個功能的實現,最終的效果變成:app

點擊歷史列表某一項時,這一項的文字加粗

在state裏面加一個字段selectedIndex,代表如今選擇的是哪個歷史項,這個字段會在渲染的時候使用。post

this.state = {
      history: [
        {
          squares: Array(9).fill(null),
        },
      ],
      currentStep: 0,
      selectedIndex: -1,
      xIsNext: true
    };
複製代碼

在渲染的時候,代碼改爲這樣:ui

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>
      )
    })
複製代碼

在生成整個歷史列表時,要遍歷每一項,這個時候,判斷狀態裏面保存的選擇的下標是否是跟某一項的下標相等,若是相等就在文字外面包一個加粗的標籤。

在點擊某一項時,須要設置selectedIndex的值,代碼變成這樣子:

jumpTo(i) {
    this.setState({
      currentStep: i,
      selectedIndex: i,
      xIsNext: (i & 1) === 0
    });
  }
複製代碼

將原來硬編碼寫的九宮格改爲用循環

這裏有點坑,原本我想用雙重循環的,好比這樣:

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>
    );
複製代碼

可是在這一行:

board.push(<div className="board-row">)
複製代碼

它認爲我沒閉合標籤,認爲下面的代碼仍是內容,會報錯, 因此最後我只能改爲用map來寫了,雙重循環應該是能夠的,剛入門,不熟悉。

最後用map實現的代碼長這樣:

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>
    );
複製代碼

若是是平局,在界面上顯示出來

這個很簡單,直接加一個判斷,若是沒有計算出來winner,而且所有棋子都擺滿了棋盤,就認爲是平局。代碼以下:

if (winner) {
      status = `Winner: ${winner}`
    } else if (history.length > 9) {
      status = `No Winner, it's draw` } 複製代碼

回放功能

實現也比較簡單,思路就是用history保存的狀態,用setInterval從新設置一遍,在遍歷到最後一個狀態時用clearInterval中止。

首先,加個按鈕:

<div className="game-info">
          <div>{status}</div>
          <ol>{moves}</ol>
          <div><button onClick={() => this.playBack()}>Play back</button></div>
        </div>
複製代碼

而後看這個按鈕調用的邏輯:

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)
  }
複製代碼

整個實現就是這樣的了!忘了要把源碼附上,在這裏

本文首發於個人博客

相關文章
相關標籤/搜索