文档概念看得迷迷糊糊,转身就忘,还是自己敲吧,跟着官方文档来一遍棋盘练手。
import React from 'react';
import ReactDOM, { createPortal } from 'react-dom';
import './index.css';
// 5.9:状态提升(子组件不再单独保留state状态,而是提升到公共的父组件进行统一维护和修改)
// 5.9:函数式组件(没有state,只有render函数,写法比类组件简单),不需要写render,直接return;没有this,直接props;没有生命周期
// 5.9:双方轮流落子,定义布尔值变量控制切换并更新布尔值
// 5.9:点击已填充的仍会切换,进行if判断只在未填充时进行修改 if(xxx) return
// 5.9:判断胜者,遍历成功的数组
// 5.10:状态提升 board保存state提升到game组件 stepNumber控制步骤
// 5.10:时间旅行
// 5.12:游戏历史记录列表显示每一步棋的坐标,格式为 (列号, 行号) 程序员都是从0开始计数的! 列号=索引%总列数 行号=parseInt(索引/总列数)
// 5.13:添加一个可以升序或降序显示历史记录的按钮,新增布尔值绑定ol标签的reverse属性列表的条目是否倒序 切换顺序 temphistory 注意不再根据index判断 moves中desc的渲染 step存储每步序号 注意reverse直接改变原数组
// 5:13:每当有人获胜时,高亮显示连成一线的 3 颗棋子。 props传递winnerline
// 5.13:当无人获胜时,显示一个平局的消息 点击时stepNumber为9
// // Square类组件
// class Square extends React.Component {
// render () {
// // console.log(this) // Square实例
// return (
// <button className="square" onClick={this.props.onClick}>
// {this.props.value}
// </button>
// );
// }
// }
// Square函数式组件
function Square (props) {
return (
<button className={`square ${props.isWinner ? 'winner ' : ''}`} onClick={props.onClick}>
{props.value}
</button>
)
}
class Board extends React.Component {
renderSquare (i) {
return <Square key={i} value={this.props.squareArr[i]} isWinner={this.props.winnerLine && this.props.winnerLine.includes(i)} onClick={() => this.props.onClick(i)} />;
}
render () {
const finalArr = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
]
return (
<div>
{
finalArr.map((outter, index) => {
return (
<div key={index} className="board-row">
{
outter.map(item => { return this.renderSquare(item) })
}
</div>
)
})
}
</div>
);
}
}
class Game extends React.Component {
constructor(props) {
super(props)
this.state = {
history: [
{
squareArr: Array(9).fill(null),
currentXY: null,
step: 0
}
],
XIsNext: true,
stepNumber: 0,
isReversed: false
}
}
render () {
let history = this.state.history
const current = history[this.state.stepNumber]
// debugger
const { winner, winnerLine } = this.caculateWinner(current.squareArr)
const status = winner ? 'Winner' + winner : 'Next player: ' + (this.state.XIsNext ? 'X' : 'O')
const tempHistory = history.slice()
if (this.state.isReversed) {
tempHistory.reverse()
}
const moves = tempHistory.map((item, index) => {
var desc = item.currentXY || item.currentXY === 0 ? `Go To Move #${item.step} Col: ${parseInt(item.currentXY % 3)} Row: ${parseInt(item.currentXY / 3)}` : 'Go To game start'
return (
<li key={item.currentXY}> <button onClick={() => this.jumpTo(item.step)} className={item.step === this.state.stepNumber ? 'active' : null} >{desc}</button></li>
)
})
// 渲染不对肯定是board接收的数据不对 debug这么费劲吗 不动脑子 自己气自己
return (
<div className="game">
<div className="game-board">
<Board squareArr={current.squareArr} winnerLine={winnerLine} onClick={(i) => this.handleClick(i)} />
</div>
<div className="game-info">
<button onClick={() => { this.handleToggleSort() }}>{this.state.isReversed ? '升序' : '降序'}</button>
<div>{status}</div>
<ol reversed={this.state.isReversed}>{moves}</ol>
</div>
</div>
);
}
handleClick (i) {
const history = this.state.history.slice(0, this.state.stepNumber + 1)
const current = history[history.length - 1]
let squareArr = current.squareArr.slice()
if (this.caculateWinner(squareArr).winner || squareArr[i] && this.state.stepNumber < 9) {
return
}
squareArr[i] = this.state.XIsNext ? 'X' : 'O'
this.setState({
history: history.concat({ squareArr: squareArr, currentXY: i, step: this.state.stepNumber + 1 }),
XIsNext: !this.state.XIsNext,
stepNumber: history.length
}, () => {
setTimeout(() => {
if (!this.caculateWinner(squareArr).winner && this.state.stepNumber >= 9) {
console.log(this.state.stepNumber)
alert('平局')
}
}, 0)
})
// TODO:这种会先弹出对话框再更新页面 不是想要的结果 应该先更新页面再弹出对话框 有没有除上一种之外的方案呢?
// this.setState({
// history: history.concat({ squareArr: squareArr, currentXY: i, step: this.state.stepNumber + 1 }),
// XIsNext: !this.state.XIsNext,
// stepNumber: history.length
// }, () => {
// if (!this.caculateWinner(squareArr).winner && this.state.stepNumber >= 9) {
// console.log(this.state.stepNumber)
// alert('平局')
// }
// })
}
caculateWinner (squareArr) {
let winnerArr = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
]
for (let i = 0; i < winnerArr.length; i++) {
let [a, b, c] = winnerArr[i]
if (squareArr[a] && squareArr[a] === squareArr[b] && squareArr[a] === squareArr[c]) {
return { winner: squareArr[a], winnerLine: winnerArr[i] }
}
}
return { winner: null }
}
jumpTo (index) {
let history = this.state.history
this.setState({
stepNumber: index,
XIsNext: index % 2 === 0 ? 'O' : 'X'
// 不在这里set history在点击的棋盘的时候才进行更新 如果在这里更新history那么moves也会更新 就不能任意切换了
})
}
handleToggleSort () {
this.setState({
isReversed: !this.state.isReversed
})
}
}
// ========================================
ReactDOM.render(
<Game />,
document.getElementById('root')
);