Tic-Tac-Toe is a classic two-player game, X and O, who take turns marking the spaces in a 3x3 grid. The player who succeeds in placing three of their marks in a horizontal, vertical, or diagonal row wins the game. If all nine squares are filled and no player has three marks in a row, the game is a draw.
Implementing Tic-Tac-Toe in React involves breaking down the UI into reusable components and managing the game's state using React hooks. This modular approach makes the game logic clear and the UI responsive.
Key concepts in the React implementation:
1. Components:
* `Square`: A single button representing one of the nine cells on the board. It displays either 'X', 'O', or is empty. It receives its `value` (what to display) and an `onClick` handler as props.
* `Board`: Renders the 3x3 grid of `Square` components. It receives the current `squares` (the board's state) and the `onClick` handler (for individual squares) from its parent.
* `Game`: The top-level component that orchestrates the entire game. It manages the game's state and renders the `Board` component. It also contains the core logic for determining the winner, handling player turns, and allowing game resets.
2. State Management (`useState`):
* `board`: An array of 9 elements (initially `null` for empty squares) to represent the state of the 3x3 grid. Each element will hold 'X', 'O', or `null`.
* `xIsNext`: A boolean flag indicating whose turn it is. `true` means 'X' is the current player, `false` means 'O'. This toggles between turns.
3. Game Logic:
* `handleClick(i)`: This function is triggered when a `Square` is clicked. It first checks if the game is already won or if the clicked square is already occupied. If not, it updates the `board` state with the current player's mark at index `i` and then toggles `xIsNext` for the next turn.
* `calculateWinner(board)`: A pure helper function that takes the current `board` array and checks all possible winning lines (three rows, three columns, two diagonals). If a player has three identical marks in any of these lines, it returns 'X' or 'O'; otherwise, it returns `null`.
4. Displaying Game Status: The `Game` component dynamically displays messages to the user, such as "Next player: X", "Winner: O", or "It's a Draw!", based on the current game state.
5. Reset Functionality: A "Start New Game" button allows players to reset the `board` and `xIsNext` states, effectively starting a fresh game.
Example Code
import React, { useState } from 'react';
// Square Component: Represents a single cell on the Tic-Tac-Toe board.
function Square({ value, onClick }) {
return (
<button
className="square"
onClick={onClick}
style={{
background: '#fff',
border: '1px solid #999',
float: 'left',
fontSize: '24px',
fontWeight: 'bold',
lineHeight: '34px',
height: '34px',
marginRight: '-1px',
marginTop: '-1px',
padding: '0',
textAlign: 'center',
width: '34px',
cursor: 'pointer'
}}
>
{value}
</button>
);
}
// Board Component: Renders the 3x3 grid of squares.
function Board({ squares, onClick }) {
const renderSquare = (i) => {
return <Square value={squares[i]} onClick={() => onClick(i)} />;
};
return (
<div>
<div className="board-row" style={{ clear: 'both', content: '""', display: 'table' }}>
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="board-row" style={{ clear: 'both', content: '""', display: 'table' }}>
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="board-row" style={{ clear: 'both', content: '""', display: 'table' }}>
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
</div>
);
}
// Game Component: Manages the overall game state and logic.
function Game() {
const [board, setBoard] = useState(Array(9).fill(null));
const [xIsNext, setXIsNext] = useState(true);
const winner = calculateWinner(board);
const isDraw = board.every(square => square !== null) && !winner; // Check for draw only if no winner
const status = winner
? 'Winner: ' + winner
: isDraw
? 'It\'s a Draw!'
: 'Next player: ' + (xIsNext ? 'X' : 'O');
const handleClick = (i) => {
// If winner exists or square is already filled, do nothing
if (winner || board[i] || isDraw) {
return;
}
const newBoard = board.slice(); // Create a shallow copy of the board array
newBoard[i] = xIsNext ? 'X' : 'O';
setBoard(newBoard);
setXIsNext(!xIsNext);
};
const handleRestart = () => {
setBoard(Array(9).fill(null));
setXIsNext(true);
};
return (
<div className="game" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: '20px', fontFamily: 'Arial, sans-serif' }}>
<h1 style={{color: '#333'}}>Tic-Tac-Toe</h1>
<div className="game-board">
<Board squares={board} onClick={handleClick} />
</div>
<div className="game-info" style={{ marginTop: '20px' }}>
<div style={{ marginBottom: '10px', fontSize: '20px', fontWeight: 'bold', color: '#555' }}>{status}</div>
<button
onClick={handleRestart}
style={{
padding: '10px 20px',
fontSize: '16px',
cursor: 'pointer',
backgroundColor: '#4CAF50',
color: 'white',
border: 'none',
borderRadius: '5px',
outline: 'none'
}}
>
Start New Game
</button>
</div>
</div>
);
}
// Helper function to determine the winner of the game
function calculateWinner(squares) {
const lines = [
[0, 1, 2], // row 1
[3, 4, 5], // row 2
[6, 7, 8], // row 3
[0, 3, 6], // col 1
[1, 4, 7], // col 2
[2, 5, 8], // col 3
[0, 4, 8], // diagonal
[2, 4, 6], // diagonal
];
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;
}
export default Game;








Game (Tic-Tac-Toe)