-
Notifications
You must be signed in to change notification settings - Fork 0
Home
This training module is designed to get you started with React by briefly covering some of the fundamental concepts and then demonstrate how to build a basic TicTacToe application with React. It will also introduce Vite, a front-end toolchain that makes developing with React quick and easy.
- Node.js (v16 or higher)
- You can check your installation and version by typing
node -v
in a terminal. - You can download Node.js here
- You can check your installation and version by typing
- Visual Studio Code (Recommended)
- Basic understanding of JS object destructuring assignment and arrow functions
- Create a starter project with Vite
- Use JSX format to dynamically render HTML
- Create and use a function component
- Understand the lifecycle of a component
- Understand state management using React Hooks
In this section of the training module we are going to develop a simple TicTacToe application that leverages many of the React concepts that were explained above.
Code snippets provided in this tutorial may contain comments // like this...
indicating the location of surrounding code. These comments are purely to help provide context and do not need to be copied into your project.
Start off by downloading our starter files to your local machine, which contain a barebones React/Vite app containing a bit of the boilerplate code and CSS styles for the project.
Open the project in VSCode and open a terminal by clicking Terminal->New Terminal
at the top of your screen.
Run the following command in your terminal to install the necessary dependencies:
npm install
If you get an error here, you most likely do not have Node.js properly installed on your machine, or it is out of date.
Next, we'll start a live development server by running the following command so that we can see our changes locally whenever we save:
npm run dev
The basic concept is to store our X and O placement data in an array and use React to convert that into the 3x3 board on the screen.
The first thing we will need to do is create a variable containing our board's current state.
To do that, we will use the useState
hook to allow this data to persist between renders.
Add the following line at the top inside the App function component in App.jsx
:
// function App() {
const [squares, setSquares] = useState(new Array(9).fill(null));
We are going to use a helper function within our App component to create a series of 9 Square
components to represent this array. Take note of the Square.jsx
file, which contains a nearly empty component, and how we have already imported it in App.jsx
using destructuring syntax:
// App.jsx
import { Square } from "./Square";
Add the following function inside the App component:
// const [squares, ...
function renderSquares() {
const arr = [];
for (let i = 0; i < squares.length; i++) {
arr.push(
<Square
key={i}
idx={i}
value={squares[i]}
/>
);
}
return arr;
}
// return (...
Few things to note here. We are using JSX inside of the arr.push()
method, showing that you can utilize JSX elements anywhere within your code. In particular, we have a <Square />
component and are passing a few parameters (properties) with its construction:
-
key
: React requires this parameter whenever you have a bunch of components in an array-like structure. We can't access it directly. -
idx
: Because we can't access the key ourselves, we need to set an extra property to track the element's index in the array. -
value
: This will be what the Square renders inside of it on the screen.
Now, lets add this function inside of our App's return statement JSX, in the board
div:
<div className="board">
{renderSquares()}
</div>
Your app should now look something like this:
Next we need to add our properties to the Square.jsx
and make them display their proper value.
Change the Square component declaration line to look like this:
// Square.jsx
export const Square = ({ idx, value }) => {
Replace the content inside the return statement div with the value
property:
// return (
<div className="square">
{value}
</div>
We need another variable to determine which player's turn it is. Just like with our board squares, we are going to use another useState
hook with a default value of true
.
Add the following lines to the top of your App component:
// const [squares, ...
const [turnX, setTurnX] = useState(true)
We're going to make a function within App handle what happens whenever a square gets clicked, and then pass that function to our Square
component.
Let's add the function to our App now:
function squareClicked(idx) {
setSquares((oldSquares) => {
const newSquares = [...oldSquares];
newSquares[idx] = turnX ? "X" : "O";
return newSquares;
});
setTurnX((current) => !current);
}
So what's going on here? We are using the setSquares
function to update our squares
state variable. Since we want to base the new value off of the old value, we pass in a function, copy the array, modify it based on who's turn it is, and return the new array to be the board's state.
Next, we use setTurnX
with the same function syntax (the return
is implied here because it is one-line) to flip the boolean variable and change turns.
Pass the function as a property to our Square component inside of our renderSquares
function by adding this line to our <Square />
JSX:
// arr.push(
// <Square
// ...
handleClick={squareClicked}
// /> ...
In Square.jsx
, we need to accept the new property and utilize it in a onClick
HTML property in our div. We'll give the onClick
property an inline function that only calls handleClick
if the square is empty.
Apply these changes by updating the Square.jsx file like this:
// Square.jsx
export const Square = ({ idx, value, handleClick }) => {
return (
<div
className="square"
onClick={() => (value ? null : handleClick(idx))}
>
{value}
</div>
);
};
You should now be able to test the functionality of clicking on a square and watching the alternating 'X's and 'O's appear as you click. But how do we know who's turn it is? Let's add a display for that now.
Change the <h2>
element in the App return JSX to look like this:
// App.jsx
// <h1 className="title"> ...
<h2>
Player Turn:
<span className="turnIndicator">{turnX ? "X" : "O"}</span>
</h2>