返回
React井字棋游戏改进列表实现
前端
2023-11-05 04:07:20
改进列表
1. 用枚举代替字符串表示方格状态
原先,我们使用字符串"X"和"O"来表示方格的状态。这很容易出错,因为我们可能会不小心拼错字符串,或者忘记在字符串前面加上引号。为了避免这种情况,我们可以使用枚举来表示方格的状态。
enum SquareState {
EMPTY,
X,
O,
}
这样,我们就不会再犯上述错误了。
2. 创建自定义组件
原先,我们将游戏逻辑和UI代码都放在一个组件中。这使得代码很难维护和扩展。为了解决这个问题,我们可以创建自定义组件来封装游戏逻辑和UI代码。
const Square = ({ value, onClick }) => {
return (
<button className="square" onClick={onClick}>
{value}
</button>
);
};
const Board = ({ squares, onClick }) => {
return (
<div className="board">
{squares.map((row, i) => (
<div key={i} className="row">
{row.map((square, j) => (
<Square key={j} value={square} onClick={() => onClick(i, j)} />
))}
</div>
))}
</div>
);
};
这样,我们的代码就更加模块化和可重用了。
3. 使用React Context
原先,我们将游戏状态存储在组件的state中。这使得我们很难在不同的组件之间共享游戏状态。为了解决这个问题,我们可以使用React Context来共享游戏状态。
const GameContext = React.createContext();
const GameProvider = ({ children }) => {
const [squares, setSquares] = useState(Array(9).fill(SquareState.EMPTY));
const [currentPlayer, setCurrentPlayer] = useState(SquareState.X);
return (
<GameContext.Provider value={{ squares, setSquares, currentPlayer, setCurrentPlayer }}>
{children}
</GameContext.Provider>
);
};
这样,我们就可以在任何组件中使用游戏状态了。
4. 添加AI对手
原先,游戏只能由两个人类玩家进行。为了让游戏更具挑战性,我们可以添加一个AI对手。
const AI = () => {
const { squares, setSquares, currentPlayer } = useContext(GameContext);
const bestMove = minimax(squares, currentPlayer);
useEffect(() => {
if (currentPlayer === SquareState.O) {
setTimeout(() => {
setSquares((prevSquares) => {
const newSquares = [...prevSquares];
newSquares[bestMove] = SquareState.O;
return newSquares;
});
}, 1000);
}
}, [currentPlayer]);
return null;
};
这样,我们的游戏就拥有了一个AI对手了。
5. 提供游戏历史记录和回放功能
原先,游戏没有提供游戏历史记录和回放功能。这使得我们很难回顾游戏的过程,并从中学习。为了解决这个问题,我们可以提供游戏历史记录和回放功能。
const GameHistory = () => {
const { squares, setSquares } = useContext(GameContext);
const [history, setHistory] = useState([]);
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
setHistory((prevHistory) => {
const newHistory = [...prevHistory];
newHistory.push(squares);
return newHistory;
});
}, [squares]);
const handleStepClick = (index) => {
setCurrentIndex(index);
setSquares(history[index]);
};
return (
<div className="game-history">
<ul>
{history.map((square, index) => (
<li key={index}>
<button onClick={() => handleStepClick(index)}>
{index + 1}
</button>
</li>
))}
</ul>
</div>
);
};
这样,我们的游戏就拥有了游戏历史记录和回放功能了。
6. 使用持久化存储
原先,游戏的状态只存储在内存中。这使得游戏无法在刷新页面后继续进行。为了解决这个问题,我们可以使用持久化存储来存储游戏的状态。
const useLocalStorage = (key, initialValue) => {
const [value, setValue] = useState(() => {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};
const Game = () => {
const [squares, setSquares] = useLocalStorage("squares", Array(9).fill(SquareState.EMPTY));
const [currentPlayer, setCurrentPlayer] = useLocalStorage("currentPlayer", SquareState.X);
return (
<GameProvider value={{ squares, setSquares, currentPlayer, setCurrentPlayer }}>
<div className="game">
<Board squares={squares} onClick={(i, j) => handleClick(i, j)} />
<GameHistory />
</div>
</GameProvider>
);
};
这样,我们的游戏就拥有了持久化存储功能了。
结语
通过这些改进,React井字棋游戏变得更加健壮、易于维护、功能更加完善。这些改进不仅可以帮助我们学习React,还可以帮助我们学习如何构建一个完整的Web应用程序。