When I started on my React journey, I started with a course on Udemy, and then another one on YouTube, and then another one on Coursera - and let's be honest, there are only so many videos that you can watch and follow along with before you start feeling unchallenged.
Yes, learning to code is super cool, but trying to do it on your own is pretty terrifying. That's why I ditched the videos, courses, and lengthy tutorials (not including documentation) and took the first steps towards coding React on my own: by making my own tic-tac-toe game.
Tic-Tac-Toe games are already so overdone, but when you're learning a new programming language, it is good to try anything that will teach you the basics and more. Practice does make perfect, and if not perfect, definitely better.
Now, let me share my knowledge with you - future React Master! 😊
All explanations for the project are in the code itself via the comments, but if you get stuck or want to view my CSS file, use the images or custom fonts, check it out on my GitHub Repository.
Pre Setup - Installing Packages
To complete this project exactly as (or however you want) I did, you will need to do the following in your preferred command line:
npx create-react-app tic-tac-react
npm i react-bootstrap bootstrap --save
cd tic-tac-react
Step 1 - Initial Setup
Set up your App.JS file to contain the following component files. This being said, you can create a components folder in your ./src folder for the following files: Board.js, Game.js, Square.js, Header.js.
//App.js
import React from 'react';
import Game from './components/Game';
import Header from './components/Header';
import 'bootstrap/dist/css/bootstrap.min.css';
//Exports the App function to be used in index.js
function App() {
return (
<div className="App">
<Header />
<Game />
</div>
);
}
//Exports App Component to be used in index.js`
export default App;
Step 2 - Creating The Squares
To be able to select values in the board that we will create later on, we first need to render the Squares which will contain the "x" or "o" values.
We do the following in the Square.js file:
//Square.js
import React from 'react';
//The Square component function a single <button>
function Square(props) {
return (
//onClick handler that will re-render the Square value whenever the <button> is clicked.
<button className="square" onClick={props.onClick}>
{/* this will call the value passed by the renderSquare component into the square, x or o*/}
{props.value}
</button>
);
}
//Exports Square Component to be used in app.js
export default Square;
Step 3 - Creating The Board
Now the board will be our main interface to the game, which we will make functional in the next step. We now need to render the square buttons created to the board of the game.
We do the following in the Board.js file:
//Board.js
import React from 'react';
import Square from './Square';
import {Container, Row} from 'react-bootstrap';
//Board renders 9 squares to compose the tic-tac-toe board
class Board extends React.Component {
//Pass the props to render the square number value to the board
renderSquare(i) {
/* this will pass a value (x, o, or null) to the Square */
return (
<Square
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
/>
);
}
//Board rendering with square value init
render() {
//this will set the render the board
return (
<Container>
<Row>
<div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
</Row>
</Container>
);
};
}
//Exports Board Component to be used in app.js
export default Board;
Step 4 - Creating the Final Game
Now that we have created the board and the squares, we need to add the functionality to our actual game.
We do the following in the Game.js file:
//Game.js
import React from 'react';
import Board from './Board';
import {Button, Container, Row, Col} from 'react-bootstrap';
//The Game component renders a board which adds functionality to the game
class Game extends React.Component {
constructor(props) {
super(props);
this.state = {
//sets the Board's initial state to contain an array of 9 nulls on 9 squares
history: [{
squares: Array(9).fill(null),
}],
//Indicates which step we’re currently viewing.
stepNumber: 0,
//xIsNext (a boolean) will be flipped to determine which player goes next and the game’s state will be saved
xIsNext: true,
}
}
//sets the state of the clicked square to an X value
handleClick(i) {
//ensures we don’t get stuck showing the same move after a new one has been made.
const history = this.state.history.slice(0, this.state.stepNumber + 1);
const current = history[history.length - 1];
const squares = current.squares.slice();
//returns early by ignoring a click if someone has won the game or if a Square is already filled:
if (calculateWinner(squares) || squares[i]) {
return;
}
squares[i] = this.state.xIsNext ? 'X' : 'O';
//will either set the state of the clicked block to be x, or negate it to o
this.setState ({
history: history.concat([{
squares: squares
}]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext
});
}
//update that stepNumber to its current step and that the number of the step is even
jumpTo(step) {
this.setState({
stepNumber: step,
xIsNext: (step % 2) === 0,
})
}
render() {
// uses the most recent history entry to determine and display the game’s status
const history = this.state.history;
const current = history[this.state.stepNumber];
const winner = calculateWinner(current.squares);
//For each move in the tic-tac-toe game’s history, we create a list item <li> which contains a button <button>.
const moves = history.map((step, move) => {
//display the current move and history upon click
const desc = move ?
'Return To Move #' + move :
'Reset Game Board ';
return (
//we assign a key to the move list to make each move unique so it cannot be re-ordered, deleted or inserted
<li key={move}>
<Button className="btn-prim" size="lg" onClick={() => this.jumpTo(move)}>{desc}</Button>
</li>
);
});
let status;
if (winner) {
status = 'Congrats! The winner is: ' + winner;
} else {
status = 'Player Turn: ' + (this.state.xIsNext ? 'X' : 'O');
}
return (
<Container>
<Row className="col-12">
<div className="game-space"></div>
</Row>
<Row className="col-12">
<div className="game">
<Col className="col-12 col-md-6 col-lg-6">
<div className="game-board">
<Board
squares={current.squares}
onClick={i => this.handleClick(i)}
/>
</div>
</Col>
<Col className="col-12 col-md-6 col-lg-6">
<div className="game-info">
<div className="game-status">{status}</div>
<ol className="game-moves">{moves}</ol>
</div>
</Col>
</div>
</Row>
</Container>
);
}
}
//This will calculate the winner of the game after all possible moves are used
function calculateWinner(squares) {
//possible winning moves for array of 9 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],
];
//Given an array of 9 squares, this function will check for a winner and return 'X', 'O', or null as appropriate.
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;
};
//Exports Game Component to be used in app.js
export default Game;
Step 5 - Final Touches
Now you can add the Header file, which is completely optional, but I prefer to separate my layout files from my functional components.
Instead of putting this one in the /components folder, you can also create a new folder called /layout and store your Header.js in there.
//Header.js
import React from 'react';
import {Container, Row} from 'react-bootstrap';
//The Header component which will be main ui of game
function Header() {
return (
WELCOME TO
Tic Tac React!
);
}
//Exports Header Component to be used in app.js
export default Header;
Step 6 - CSS and Deploy!
Good job, you've completed the tutorial. Now you can test your game with the following command:
npm start
Remember to add some CSS or styling to your application otherwise it's a definite uggo. You can see what I did on my CSS files in my GitHub repository listed above.
Your final project should look something like this:
As I said, I'm no React expert, but I hope I could show you something new/different today! Let me know, or teach me something new, I'd love to hear from you in the comments.😊