Tic-Tac-Toe using React hooks

I passionate Web Developer and a Competetive Programmer who is also a pre-final year in her graduation from Lovely Professional University.
You must have played tic-tac-toe in your childhood, and it was fun, right? But have you ever thought about how much fun it will be if we create our own game of tic-tac-toe? Well, today we'll create a tic-tac-toe game using React hooks in the most simple way possible.
You can watch a live demo of the project Here
A step-by-step explanation
1.Create a React app
npx create-react-app tic-tac-toe
cd tic-tac-toe
npm start
If you are using yarn, use yarn create react-app todo-list cd into the app and yarn start.
2.App.js
The code in App.js file will contain all the important components. Here is an explanation of each line of the code:
import {useState} from 'react'
import './App.css';
<style>
@import url('https://fonts.googleapis.com/css2?family=Shantell+Sans:ital,wght@1,500&display=swap');
</style>
These lines import the useState hook from the react library and import the CSS styles from the App.css file.function App() { const [turn, setturn] = useState('X'); const [cells, setcells] = useState(Array(9).fill('')); const [winner, setwinner] = useState();
function App() {
const [turn, setturn] = useState('X');
const [cells, setcells] = useState(Array(9).fill(''));
const [winner, setwinner] = useState();
This creates a React component called App, which uses the useState hook to define three state variables: turn, cells, and winner.
turn is used to keep track of whose turn it is - 'X' or 'O'. It is initialized to 'X'.
cells is an array that holds the current state of the tic-tac-toe board. It is initialized to an array of 9 empty strings.
winner is used to keeping track of the winner of the game. It is initially set to undefine.
const handleClick = (num)=>{
if(winner){
return;
}
let square = [...cells]
if(cells[num] !== ''){
alert('already clicked')
return;
}
if(turn === 'X'){
square[num] = 'X';
setturn('O')
}
else{
square[num]= 'O'
setturn('X');
}
isDraw(square)
setcells(square);
Checkwinner(square);
}
This function handleClick is called when a square is clicked on the tic-tac-toe board. It takes the index of the clicked square (num) as its argument.
It first checks whether there is already a winner - if there is, it returns without doing anything.
Then it creates a copy of the cells array, and checks whether the clicked square has already been filled - if it has, it displays an alert message and returns without doing anything.
If the square is empty, it updates the square array with the current player's symbol ('X' or 'O') and updates the turn state variable to switch to the other player's turn. Then it calls the isDraw function to check whether the game has resulted in a draw, and updates the cells state variable and checks for a winner by calling the Checkwinner function. It is placed here to check after each move if we found our winner.
const Checkwinner = (square) =>{
const combo =[ [0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
let count=0;
for(let i=0;i<combo.length; i++){
const [a,b,c] = combo[i];
if(square[a] !== '' && square[a] === square[b] && square[b]===square[c]){
setwinner(square[a]);
}
}
}
This function Checkwinner is used to check whether a player has won the game. It takes the current state of the tic-tac-toe board as its argument (square). It checks all the possible combinations of squares that could result in a win and sets the winner state variable to the appropriate player if a win is detected.
const handleRestart =() =>{
setwinner('');
setcells(Array(9).fill(''));
setturn('X');
}
const isDraw =(cells) =>{
let count=0;
cells.forEach(element => {
if(element !== ''){
count++;
}
});
if(count >= 9){
setwinner('Draw')
}
}
handleRestart is a function that resets the game when the "Play Again" button is clicked. It calls setwinner('') to clear the state of winner, setcells(Array(9).fill('')) to reset the state of cells to an array of 9 empty strings, and setturn('X') to set the turn back to "X" to start a new game.
isDraw is a function that checks if the game has ended in a draw. It takes cells as its parameter, which is an array representing the current state of the game board. It initializes count to 0 and loops through each element of the cells array. For each non-empty element, it increments count. If count is greater than or equal to 9, it means all the cells are filled and no player has won, so it calls setwinner('Draw') to set the state of winner to "Draw".
const Cells =({num}) =>{
return <td onClick={() => handleClick(num)}>{cells[num]}</td>;
}
Cells is a functional component that represents a single cell in the tic-tac-toe game board. It takes a prop num as its parameter, which is a number representing the index of the cell in the cells array.
The component returns a td element with an onClick event handler that calls the handleClick function with the num prop as its argument when the cell is clicked. It also displays the value of the cell, which is the corresponding element in the cells array, using cells[num].
return (
<div className="container">
<h1>Tic Tac Toe</h1>
<h2> Turn: {turn}</h2>
<table>
{/* <h2> Turn: {turn}</h2> */}
<tbody>
<tr>
<Cells num={0} />
<Cells num={1} />
<Cells num={2}/>
</tr>
<tr>
<Cells num={3}/>
<Cells num={4}/>
<Cells num={5}/>
</tr>
<tr>
<Cells num={6}/>
<Cells num={7}/>
<Cells num={8}/>
</tr>
</tbody>
</table>
{/* console.log({winner}) */}
{winner && <>
{winner === 'Draw' ? <>
<h2>Match Draw</h2>
<button onClick={handleRestart}>Play Again</button>
</> : <>
<h2>{winner} is the winner</h2>
<button onClick={handleRestart}>Play Again</button>
</>}
</> }
</div>
);
This is the final return statement of the Tic Tac Toe game component. It returns a container div with a h1 heading displaying "Tic Tac Toe" and a h2 displaying the current turn (i.e., "Turn: X" or "Turn: O").
Inside the container, there is a table with a tbody element containing three rows, each with three td elements representing the individual cells of the game board. These td elements are created using the Cells component and pass in a num prop representing the index of the corresponding cell in the cells state array.
Below the table, there is conditional rendering logic that displays a message indicating the winner (if there is one) and a "Play Again" button. The winner state variable is used to determine if this message and button should be displayed. If winner value is not null, the message and button are displayed. If winner is null, they are not displayed.
If winner is "Draw", the message displayed is "Match Draw" instead of "{winner} is the winner". This is because when the game ends in a draw, there is no clear winner.
3.App.css
body {
background-color: #2c3e50;
color: #fff;
/* font-family: sans-serif; */
margin: 0;
padding: 0;
font-family: 'Shantell Sans', cursive;
}
.container{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
table{
background-color: #6684a1;
}
table td{
/* border: 1px solid rgb(231, 222, 222); */
border-collapse: collapse;
background-color: #34495e;
height: 100px;
width: 100px;
text-align: center;
font-size: 50px;
}
button{
padding: 15px 20px;
border: 1px solid black ;
background-color: rgb(4, 102, 4);
color: #fff;
font-size: 1.2rem;
cursor: pointer;
}
The app is now complete and working. You can deploy your project on GitHub or Netlify. Here, is the source code for your reference: Source Code
Thank you for reading :D Hope you liked it!!

