官网地址:https://react.docschina.org/learn/tutorial-tic-tac-toe
自己做小练习时没有看官方的解题,还是蛮有成就感的hhhhh~(官方的做法是把可以胜利的情况都列出来,但是感觉我这个方法似乎扩展性更强一些 ~~~/狗头)
import { useState } from "react"
// 格子面板
function Board ({ allData, onChangeSquare }) {
return (
<div className={'board'} style={{ width: '110px' }}>
{allData.map((val, index) => <button key={index} className="square" onClick={() => onChangeSquare(index)}>{val}</button>)}
</div>
);
}
// 记录面板
function History ({ historyList, onChangeHistory }) {
return (
<div>
{
historyList.map((history, index) => {
return (
<div key={index} onClick={() => onChangeHistory(index)}>
<span style={{ marginLeft: '70px' }}></span>{index + 1}.<button>Go to {index ? `move #${index}` : 'game start'}</button>
</div>
)
})
}
</div>
);
}
// 整体面板
export default function All () {
const [res, setRes] = useState(Array(9).fill(''))
const [nextFlag, setNextFlag] = useState('X')
const [winner, setWinner] = useState('')
const [curStep, setCurStep] = useState(0)
const [historyList, setHistoryList] = useState([res])
// 判断是否胜利
const judgeWinner = (history) => {
setWinner('')
if ((history[0] === history[1] && history[0] === history[2]) || (history[0] === history[3] && history[0] === history[6])) {
setWinner(history[0])
} else if ((history[8] === history[7] && history[8] === history[6]) || (history[8] === history[5] && history[8] === history[2])) {
setWinner(history[8])
} else {
// 以中心判断,同样间距的下标相加=9则表示在一条线上
const winnerFlag = history[4]
const indexList = history.map((val, index) => val === winnerFlag ? index : -1).filter(index => index !== -1)
for (i = 0; indexList[i] < 4; i++) {
if (history[indexList[i]] === winnerFlag && history[8 - indexList[i]] === winnerFlag) {
setWinner(winnerFlag)
}
}
}
}
// 更新面板
const updateBoard = (newVal) => {
setRes(newVal)
// 判断是否获胜
judgeWinner(newVal)
}
// 点击方块
const onChangeSquare = (index) => {
// 此处有值或已成功则不改变
if (res[index] || winner) {
return
}
// 更新对应位置
const history = [...res]
history.splice(index, 1, nextFlag)
if (curStep && curStep !== historyList.length - 1) {
// 从某一条记录进入后,点击方块则清除该条之后的记录
historyList.splice(curStep + 1)
}
// 添加记录
setHistoryList([...historyList, history])
// 更新面板
updateBoard(history)
// 判断下一步的标记
setNextFlag(nextFlag === 'X' ? 'O' : 'X')
// 每次点击方块后均初始化当前记录下标
setCurStep(0)
}
// 点击某条记录
const onChangeHistory = (index) => {
// 保存当前点击的记录下标
setCurStep(index)
if (!index) {
// 重新开始时,初始化面板/记录/获胜方
updateBoard(Array(9).fill(''))
setHistoryList([res])
setNextFlag('X')
} else {
// 跳转至某条记录时,更新面板并重新记录获胜方
updateBoard(historyList[index])
setNextFlag(index % 2 ? 'O' : 'X')
}
}
return (
<>
<p>{winner && !(curStep && curStep !== historyList.length - 1) ? `Winner:${winner}` : `Next Player:${nextFlag}`}</p>
<div style={{ display: 'flex' }}>
<Board allData={res} onChangeSquare={onChangeSquare} />
<History historyList={historyList} onChangeHistory={onChangeHistory} />
</div>
</>
);
}