前段時間換了家外企工做, 空閒時間比較多。html
雖然我是作Java後端的,可是老外喜歡搞敏捷開發和全棧,因此也要寫前端,既然是老外,那確定是喜歡用React的,然而我以前歷來沒寫過React,只能從基礎一步一步來了。前端
井字遊戲是React官方的入門實戰教程的案例來的,在這裏。react
這個遊戲的規則是這樣的:git
最後的效果是這樣的: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)
}
複製代碼
整個實現就是這樣的了!忘了要把源碼附上,在這裏
本文首發於個人博客。