Interactive Tic-Tac-Toe Game in React

Introduction

In this article, I'm going to show how to create a custom React component for a TicTacToe Game interactively in a React Application.

Requirement

  • Basic knowledge of React
  • Knowledge about useState & useEffect hooks
  • Visual Studio Code

Step 1. We need to create a new React app using the below command.

npm create-react-app tic-tac-toe

Step 2. Create a new folder named components and then a new file named TicTacToe.js and add the following code.

import { useEffect, useState } from "react";
import "./styles.css";

function Square({ value, onClick }) {
  return (
    <button onClick={onClick} className="square">
      {value}
    </button>
  );
}
export default function TicTacToe() {
  const [squares, setSquares] = useState(Array(9).fill(""));
  const [isXTurn, setIsXTurn] = useState(true);
  const [status, setStatus] = useState("");

  function getWinner(squares) {
    const winningPatterns = [
      [0, 1, 2],
      [3, 4, 5],
      [6, 7, 8],
      [2, 5, 8],
      [0, 4, 8],
      [2, 4, 6],
      [0, 3, 6],
      [1, 4, 7],
    ];
    for (let i = 0; i < winningPatterns.length; i++) {
      const [x, y, z] = winningPatterns[i];
      if (
        squares[x] &&
        squares[x] === squares[y] &&
        squares[x] === squares[z]
      ) {
        return squares[x];
      }
    }
    return null;
  }
  function handleClick(getCurrentSquare) {
    let cpySquares = [...squares];
    if (getWinner(cpySquares) || cpySquares[getCurrentSquare]) return;
    cpySquares[getCurrentSquare] = isXTurn ? "X" : "O";
    setIsXTurn(!isXTurn);
    setSquares(cpySquares);
  }
  function handleRestart() {
    setIsXTurn(true);
    setSquares(Array(9).fill(""));
  }
  useEffect(() => {
    if (!getWinner(squares) && squares.every((item) => item !== "")) {
      setStatus(`This is a draw ! Please restart the game`);
    } else if (getWinner(squares)) {
      setStatus(`Winner is ${getWinner(squares)}. Please restart the game`);
    } else {
      setStatus(`Next player is ${isXTurn ? "X" : "O"}`);
    }
  }, [squares, isXTurn]);
  console.log(squares);
  return (
    <div className="tic-tac-toe-container">
      <div className="row">
        <Square value={squares[0]} onClick={() => handleClick(0)} />
        <Square value={squares[1]} onClick={() => handleClick(1)} />
        <Square value={squares[2]} onClick={() => handleClick(2)} />
      </div>
      <div className="row">
        <Square value={squares[3]} onClick={() => handleClick(3)} />
        <Square value={squares[4]} onClick={() => handleClick(4)} />
        <Square value={squares[5]} onClick={() => handleClick(5)} />
      </div>
      <div className="row">
        <Square value={squares[6]} onClick={() => handleClick(6)} />
        <Square value={squares[7]} onClick={() => handleClick(7)} />
        <Square value={squares[8]} onClick={() => handleClick(8)} />
      </div>
      <h1>{status}</h1>
      <button onClick={handleRestart}>Restart</button>
    </div>
  );
}

Step 3. Then, create a Styles.css to add the styles for our game.

.tic-tac-toe-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-top: 100px;
  }
  
  .square {
    border: 1px solid red;
    float: left;
    font-size: 40px;
    height: 100px;
    padding: 0px;
    text-align: center;
    width: 100px;
    margin-right: -1px;
    margin-top: -1px;
    cursor: pointer;
  }

Now, we need to use the TicTacToe Component in the App.js file by removing the default and using the below code.

import './App.css';
import TicTacToe from './Components/TicTacToe/TicTacToe';
function App() {
  return (
    <div className="App">
    <TicTacToe/>
    </div>
  );
}
export default App;

Finally, you can run your application, and we can see the image slider locally in our React app.

React app

The explanation of the code is as follows.

  • Square: A functional component that renders a button for each square, displaying the current value (X or O) and calling onClick when pressed.
  • TicTacToe: The main component that manages the game state (squares, turn, and status), renders the grid, checks for a winner or draw, and allows restarting the game.
  • useState: Used to track the squares' values, the current player's turn, and the game's status message.
  • getWinner: A function that checks the board for a winning combination by comparing the current squares array with predefined winning patterns.
  • handleClick: Updates the clicked square, switches the turn, and prevents changes if the game is won or the square is occupied.
  • handleRestart: Resets the game by clearing the squares and setting the first turn to X.
  • useEffect: Updates the game status message based on the current board state, detecting a win, draw, or the next player.