Open In App

How to build a Tic-Tac-Toe Game using React Hooks ?

Last Updated : 07 Nov, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

To build a Tic-Tac-Toe using react Hooks include the interactive components that represent the board, the signs, and at last the winner of the game.

Preview of final output: Let us have a look at how the final application will look like.

Screenshot-from-2023-11-06-11-28-45

Prerequisite of Tic-Tac-Toe Game:

Approach:

To create a Tic-Tac-Toe Game in React js will use the React functional components along with React JS hooks to enable the interaction and store moves. After each move, the component will switch between players and mark X or 0 on selected part the board. Style the game board using CSS for a better appearance.

Steps to Create React Application and Install required modules: 

Step 1: Use this command in the terminal to initialize the react project.

npx create-react-app tic-tac-toe-react

Step 2: Switch to the tic-tac-toe-react directory using the following command.

cd tic-tac-toe-react

Project Structure:

Example: This example implement react components and css to build a Tic-Tac-Toe game using React Hooks.

Javascript




// Filename - App.js
 
// Importing the required components
import Board from "./Board";
import Info from "./Info";
 
// Importing the CSS File
import "./App.css";
 
// Importing the useState hook
import { useState } from "react";
 
function App() {
    // Creating a reset state, which indicates whether
    // the game should be reset or not
    const [reset, setReset] = useState(false);
 
    // Creating a winner state, which indicates
    // the current winner
    const [winner, setWinner] = useState("");
 
    // Sets the reset property to true
    // which starts the chain
    // reaction of resetting the board
    const resetBoard = () => {
        setReset(true);
    };
 
    return (
        <div className="App">
            {/* Shrinks the popup when there is no winner */}
            <div
                className={`winner ${
                    winner !== "" ? "" : "shrink"
                }`}
            >
                {/* Display the current winner */}
                <div className="winner-text">{winner}</div>
                {/* Button used to reset the board */}
                <button onClick={() => resetBoard()}>
                    Reset Board
                </button>
            </div>
            {/* Custom made board component comprising of
            the tic-tac-toe board  */}
            <Board
                reset={reset}
                setReset={setReset}
                winner={winner}
                setWinner={setWinner}
            />
            <Info />
        </div>
    );
}
 
export default App;


Javascript




// Filename - Board.js
 
// Importing the CSS for the board
import "./css/board.css";
 
// Importing the useState hook, useEffect hook and useRef hook
import { useState, useEffect, useRef } from "react";
 
const Board = ({ reset, setReset, winner, setWinner }) => {
    // Creating a turn state, which indicates the current turn
    const [turn, setTurn] = useState(0);
 
    // Creating a data state, which contains the
    // current picture of the board
    const [data, setData] = useState([
        "",
        "",
        "",
        "",
        "",
        "",
        "",
        "",
        "",
    ]);
 
    // Creating a reference for the board
    const boardRef = useRef(null);
 
    // Function to draw on the board
    const draw = (event, index) => {
        // Draws only if the position is not taken
        // and winner is not decided yet
        if (data[index - 1] === "" && winner === "") {
            // Draws X if it's player 1's turn else draws O
            const current = turn === 0 ? "X" : "O";
 
            // Updating the data state
            data[index - 1] = current;
 
            //Drawing on the board
            event.target.innerText = current;
 
            // Switching the turn
            setTurn(turn === 0 ? 1 : 0);
        }
    };
 
    // UseEffect hook used to reset the board whenever
    // a winner is decided
    useEffect(() => {
        // Clearing the data state
        setData(["", "", "", "", "", "", "", "", ""]);
 
        // Getting all the children(cells) of the board
        const cells = boardRef.current.children;
 
        // Clearing out the board
        for (let i = 0; i < 9; i++) {
            cells[i].innerText = "";
        }
 
        // Resetting the turn to player 0
        setTurn(0);
 
        // Resetting the winner
        setWinner("");
        setReset(false);
    }, [reset, setReset, setWinner]);
 
    // useEffect hook used to check for a winner
    useEffect(() => {
        // Checks for the win condition in rows
        const checkRow = () => {
            let ans = false;
            for (let i = 0; i < 9; i += 3) {
                ans |=
                    data[i] === data[i + 1] &&
                    data[i] === data[i + 2] &&
                    data[i] !== "";
            }
            return ans;
        };
 
        // Checks for the win condition in cols
        const checkCol = () => {
            let ans = false;
            for (let i = 0; i < 3; i++) {
                ans |=
                    data[i] === data[i + 3] &&
                    data[i] === data[i + 6] &&
                    data[i] !== "";
            }
            return ans;
        };
 
        // Checks for the win condition in diagonals
        const checkDiagonal = () => {
            return (
                (data[0] === data[4] &&
                    data[0] === data[8] &&
                    data[0] !== "") ||
                (data[2] === data[4] &&
                    data[2] === data[6] &&
                    data[2] !== "")
            );
        };
 
        // Checks if at all a win condition is present
        const checkWin = () => {
            return (
                checkRow() || checkCol() || checkDiagonal()
            );
        };
 
        // Checks for a tie
        const checkTie = () => {
            let count = 0;
            data.forEach((cell) => {
                if (cell !== "") {
                    count++;
                }
            });
            return count === 9;
        };
 
        // Setting the winner in case of a win
        if (checkWin()) {
            setWinner(
                turn === 0
                    ? "Player 2 Wins!"
                    : "Player 1 Wins!"
            );
        } else if (checkTie()) {
            // Setting the winner to tie in case of a tie
            setWinner("It's a Tie!");
        }
    });
 
    return (
        <div ref={boardRef} className="board">
            <div
                className="input input-1"
                onClick={(e) => draw(e, 1)}
            ></div>
            <div
                className="input input-2"
                onClick={(e) => draw(e, 2)}
            ></div>
            <div
                className="input input-3"
                onClick={(e) => draw(e, 3)}
            ></div>
            <div
                className="input input-4"
                onClick={(e) => draw(e, 4)}
            ></div>
            <div
                className="input input-5"
                onClick={(e) => draw(e, 5)}
            ></div>
            <div
                className="input input-6"
                onClick={(e) => draw(e, 6)}
            ></div>
            <div
                className="input input-7"
                onClick={(e) => draw(e, 7)}
            ></div>
            <div
                className="input input-8"
                onClick={(e) => draw(e, 8)}
            ></div>
            <div
                className="input input-9"
                onClick={(e) => draw(e, 9)}
            ></div>
        </div>
    );
};
 
export default Board;


Javascript




// Filename - Info.js
 
// Importing the css for the info
import "./css/info.css";
 
const Info = () => {
    return (
        <div className="info">
            <div className="player">Player 1: X</div>
            <div className="player">Player 2: O</div>
        </div>
    );
};
 
export default Info;


CSS




/* index.css */
 
* {
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}
 
body {
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont,
        "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
        "Cantarell", "Fira Sans", "Droid Sans",
        "Helvetica Neue", sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
 
code {
    font-family: source-code-pro, Menlo, Monaco, Consolas,
        "Courier New", monospace;
}


CSS




/* Filename - App.css */
 
@import url(
);
 
.App {
    width: 100vw;
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    gap: 5vh;
    backdrop-filter: 5px;
    background-color: #101010;
}
 
.winner {
    transition: all ease-in 0.3s;
    display: flex;
    opacity: 1;
    font-size: 1.5rem;
    font-weight: 600;
    gap: 1vh;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    width: 20vw;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -70%);
    background-color: rgba(195, 141, 158, 0.863);
    backdrop-filter: 5px;
    padding: 0.5rem;
    padding-bottom: 1rem;
    border-radius: 10%;
}
 
.winner-text {
    padding: 0.3em 1em 0.25em;
    font-weight: 600;
    font-size: 2.5rem;
    color: white;
    font-family: "Bellefair", serif;
    position: relative;
    text-align: center;
    line-height: 1.3;
}
 
.shrink {
    transform: scale(0.1);
    opacity: 0;
}
 
button {
    background-color: #111827;
    border: 1px solid transparent;
    border-radius: 0.75rem;
    box-sizing: border-box;
    color: #ffffff;
    cursor: pointer;
    flex: 0 0 auto;
    font-family: "Inter var";
    font-size: 1.125rem;
    font-weight: 600;
    line-height: 1.5rem;
    padding: 0.75rem 1.2rem;
    text-align: center;
    text-decoration: none #6b7280 solid;
    text-decoration-thickness: auto;
    transition-duration: 0.2s;
    transition-property: background-color, border-color,
        color, fill, stroke;
    transition-timing-function: cubic-bezier(
        0.4,
        0,
        0.2,
        1
    );
    user-select: none;
    -webkit-user-select: none;
    touch-action: manipulation;
    width: auto;
}
 
button:hover {
    background-color: #374151;
}
 
button:focus {
    box-shadow: none;
    outline: 2px solid transparent;
    outline-offset: 2px;
}
 
@media (min-width: 768px) {
    button {
        padding: 0.75rem 1.5rem;
    }
}


CSS




/* Filename - css/info.css */
 
.info {
    width: 30vw;
    display: flex;
    justify-content: space-evenly;
    align-items: center;
    color: whitesmoke;
}
 
.player {
    border: 2px solid #f6546a;
    border-radius: 5%;
    padding: 0.5rem 0;
    display: flex;
    font-size: 1.5rem;
    justify-content: center;
    align-items: center;
    width: 10vw;
}


CSS




/* Filename - css/board.css */
 
:root {
    --board-background: none;
    --border-color: #f6546a;
    --border-thickness: 5px;
}
 
.board {
    width: 30vw;
    height: 50%;
    background-color: var(--board-background);
    display: flex;
    align-items: flex-start;
    flex-direction: row;
    flex-wrap: wrap;
}
 
.input {
    height: 33.33%;
    width: 33.33%;
    display: flex;
    justify-content: center;
    align-items: center;
    color: whitesmoke;
    font-family: "Bellefair", serif;
    font-style: italic;
    font-weight: 700;
    font-size: 6rem;
}
 
.input-1 {
    border-right: var(--border-thickness) dashed
        var(--border-color);
    border-bottom: var(--border-thickness) dashed
        var(--border-color);
}
 
.input-2 {
    border-right: var(--border-thickness) dashed
        var(--border-color);
    border-bottom: var(--border-thickness) dashed
        var(--border-color);
}
 
.input-3 {
    border-bottom: var(--border-thickness) dashed
        var(--border-color);
}
 
.input-4 {
    border-right: var(--border-thickness) dashed
        var(--border-color);
    border-bottom: var(--border-thickness) dashed
        var(--border-color);
}
 
.input-5 {
    border-right: var(--border-thickness) dashed
        var(--border-color);
    border-bottom: var(--border-thickness) dashed
        var(--border-color);
}
 
.input-6 {
    border-bottom: var(--border-thickness) dashed
        var(--border-color);
}
 
.input-7 {
    border-right: var(--border-thickness) dashed
        var(--border-color);
}
 
.input-8 {
    border-right: var(--border-thickness) dashed
        var(--border-color);
}


Steps to Run the Application:Use this command in the terminal inside the project directory.

npm start

Output: This output will be visible on http://localhost:3000/ on the browser window.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads