📅  最后修改于: 2023-12-03 15:38:05.936000             🧑  作者: Mango
React Hooks 是 React v16.8 版本引入的一种新特性。它使得在不使用类组件的情况下,以函数式组件的形式共享 React 内部状态和生命周期方法变得更加容易。本文将介绍如何使用 React Hooks 构建一个井字游戏。
首先,需要安装 React 环境。可以使用 create-react-app 工具创建一个新项目,创建方法如下:
npx create-react-app my-app
cd my-app
npm start
创建一个 Game
函数式组件,并在组件内部需要使用的状态,和一些可复用的组件。
import React, { useState } from 'react';
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
function Board(props) {
function renderSquare(i) {
return (
<Square
value={props.squares[i]}
onClick={() => props.onClick(i)}
/>
);
}
return (
<div>
<div className="board-row">
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="board-row">
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="board-row">
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
</div>
);
}
function Game(props) {
const [history, setHistory] = useState([
{
squares: Array(9).fill(null),
},
]);
const [stepNumber, setStepNumber] = useState(0);
const [xIsNext, setXIsNext] = useState(true);
const current = history[stepNumber];
const winner = calculateWinner(current.squares);
function jumpTo(step) {
setStepNumber(step);
setXIsNext(step % 2 === 0);
}
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (xIsNext ? 'X' : 'O');
}
return (
<div className="game">
<div className="game-board">
<Board squares={current.squares} onClick={(i) => handleClick(i)} />
</div>
<div className="game-info">
<div>{status}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
创建一个函数 calculateWinner()
,用于在当前游戏状态下检查是否有一方玩家胜利。
function calculateWinner(squares) {
const lines = [
[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 < lines.length; i++) {
const [a, b, c] = lines[i];
if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
return squares[a];
}
}
return null;
}
在 Square
组件中添加一个 onClick 事件处理函数 props.onClick()
,将它传递给父组件 Board
,并将它保存在 props 中。
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}
function Board(props) {
function renderSquare(i) {
return (
<Square
value={props.squares[i]}
onClick={() => props.onClick(i)}
/>
);
}
// TODO: render squares
}
function Game(props) {
// TODO: add state hooks
function handleClick(i) {
const historySlice = history.slice(0, stepNumber + 1);
const current = historySlice[historySlice.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = xIsNext ? 'X' : 'O';
setHistory(historySlice.concat([{ squares: squares }]));
setStepNumber(historySlice.length);
setXIsNext(!xIsNext);
}
// TODO: render game
}
为了展示游戏进程,可以添加一个历史步骤列表。每个步骤都有一个按钮,点击按钮可以跳转到该步骤继续游戏。
function Game(props) {
// 状态 hooks
const [history, setHistory] = useState([
{
squares: Array(9).fill(null),
},
]);
const [stepNumber, setStepNumber] = useState(0);
const [xIsNext, setXIsNext] = useState(true);
function handleClick(i) {
// 点击事件处理函数
const historySlice = history.slice(0, stepNumber + 1);
const current = historySlice[historySlice.length - 1];
const squares = current.squares.slice();
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = xIsNext ? 'X' : 'O';
setHistory(historySlice.concat([{ squares: squares }]));
setStepNumber(historySlice.length);
setXIsNext(!xIsNext);
}
const moves = history.map((step, move) => {
const desc = move ? 'Go to move #' + move : 'Go to game start';
return (
<li key={move}>
<button onClick={() => jumpTo(move)}>{desc}</button>
</li>
);
});
function jumpTo(step) {
// 跳转到历史记录的某个步骤,继续游戏
setStepNumber(step);
setXIsNext(step % 2 === 0);
}
const current = history[stepNumber];
const winner = calculateWinner(current.squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (xIsNext ? 'X' : 'O');
}
// 渲染界面
return (
<div className="game">
<div className="game-board">
<Board squares={current.squares} onClick={(i) => handleClick(i)} />
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
使用 React Hooks 构建井字游戏,可以让代码更加简洁易懂。在函数式组件中使用 React 内部状态和生命周期方法,可以更加灵活地处理组件的状态管理和异步操作。希望本文对你有所帮助,在实践中体验 React Hooks 的强大之处!