12
Let's create a tic-tac-toe with React.
Welcome Everybody,
In this article we are going to create a tic-tac-toe app using react and react hooks.
Before we start creating our app, You should familiar
with Javscript, React and React-hooks.
So, Without wasting any time let's move to project setup.
In the terminal go to the directory where you want to create you react-app.
and then run the following command.
npx create-react-app tic-tac-toe
For creating our app I am using create-react-app
framework.
If you wanna create it manualy you can do so.
You can delete App.test.js
, setupTests.js
and logo.svg
.
After that clean App.js
like following :
import React from 'react';
import './App.css';
function App() {
return (
<div className="App">
Hello I am react app.
</div>
);
}
export default App;
Run yarn start
or npm start
for running dev server on localhost.
That's enough for project setup. Let's move to next one.
- First in
src/App.js
, Create a function calledcalculateWinner
to get winner from an array.
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
]
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
}
We are storing pattern of lines in case of a winner in lines array and if we see any of these pattern at any time of the game we will declare winner.
- For calculating next turn create a function called
calculateNextValue
.
function calculateNextValue(squares) {
return squares.filter(Boolean).length % 2 === 0 ? 'X' : 'O'
}
- then, we can check the current status of game by creating next function.
function calculateStatus(winner, squares, nextValue) {
return winner
? `Winner: ${winner}`
: squares.every(Boolean)
? `Scratch: Cat's game`
: `Next player: ${nextValue}`
}
Now in App.css
let's write some styles for our game-board
.game {
font: 14px 'Century Gothic', Futura, sans-serif;
margin: 20px;
min-height: 260px;
}
.game ol,
.game ul {
padding-left: 30px;
}
.board-row:after {
clear: both;
content: '';
display: table;
}
.status {
margin-bottom: 10px;
}
.restart {
margin-top: 10px;
}
.square {
background: #fff;
border: 1px solid #999;
float: left;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
margin-right: -1px;
margin-top: -1px;
padding: 0;
text-align: center;
width: 34px;
}
.square:focus {
outline: none;
background: #ddd;
}
.game {
display: flex;
flex-direction: row;
}
.game-info {
margin-left: 20px;
min-width: 190px;
}
Now let's create our game-board in App.js
function Board() {
const [squares, setSquares] = React.useState(Array(9).fill(null))
const nextValue = calculateNextValue(squares)
const winner = calculateWinner(squares)
const status = calculateStatus(winner, squares, nextValue)
function selectSquare(square) {
if (winner || squares[square]) {
return
}
const squaresCopy = [...squares]
squaresCopy[square] = nextValue
setSquares(squaresCopy)
}
function restart() {
setSquares(Array(9).fill(null))
}
function renderSquare(i) {
return (
<button className="square" onClick={() => selectSquare(i)}>
{squares[i]}
</button>
)
}
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="board-row">
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="board-row">
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
<button className="restart" onClick={restart}>
restart
</button>
</div>
)
}
Now create a Game()
function and put our Board()
Component
inside it.
function Game() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
</div>
)
}
then render Game()
function inside App()
.
function App() {
return <Game />
}
At the end our App.js
should look like this.
import React from 'react';
import "./App.css";
function Board() {
const [squares, setSquares] = React.useState(Array(9).fill(null))
const nextValue = calculateNextValue(squares)
const winner = calculateWinner(squares)
const status = calculateStatus(winner, squares, nextValue)
function selectSquare(square) {
if (winner || squares[square]) {
return
}
const squaresCopy = [...squares]
squaresCopy[square] = nextValue
setSquares(squaresCopy)
}
function restart() {
setSquares(Array(9).fill(null))
}
function renderSquare(i) {
return (
<button className="square" onClick={() => selectSquare(i)}>
{squares[i]}
</button>
)
}
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="board-row">
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="board-row">
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
<button className="restart" onClick={restart}>
restart
</button>
</div>
)
}
function Game() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
</div>
)
}
function calculateStatus(winner, squares, nextValue) {
return winner
? `Winner: ${winner}`
: squares.every(Boolean)
? `Scratch: Cat's game`
: `Next player: ${nextValue}`
}
function calculateNextValue(squares) {
return squares.filter(Boolean).length % 2 === 0 ? 'X' : 'O'
}
function calculateWinner(squares) {
const lines = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
]
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
}
function App() {
return <Game />
}
export default App
And that is it. We are done with that.
You can save the game's squares to browser localhost by using the following hook:
function useLocalStorageState(
key,
defaultValue = '',
{serialize = JSON.stringify, deserialize = JSON.parse} = {},
) {
const [state, setState] = React.useState(() => {
const valueInLocalStorage = window.localStorage.getItem(key)
if (valueInLocalStorage) {
return deserialize(valueInLocalStorage)
}
return typeof defaultValue === 'function' ? defaultValue() : defaultValue
})
const prevKeyRef = React.useRef(key)
React.useEffect(() => {
const prevKey = prevKeyRef.current
if (prevKey !== key) {
window.localStorage.removeItem(prevKey)
}
prevKeyRef.current = key
window.localStorage.setItem(key, serialize(state))
}, [key, state, serialize])
return [state, setState]
}
export {useLocalStorageState}
Thanks for reading.
Happy coding 😍.
12