假装学会 React
浏览了 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");
}
// ...
}
// ...
}
相关
当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »