ReactJS Tutorial
ReactJS Tutorial
You don’t have to complete all of the sections at once to get the value out of
this tutorial. Try to get as far as you can — even if it’s one or two sections.
What Are We Building?
In this tutorial, we’ll show how to build an interactive tic-tac-toe game with
React.
You can see what we’ll be building here: Final Result. If the code doesn’t
make sense to you, or if you are unfamiliar with the code’s syntax, don’t worry!
The goal of this tutorial is to help you understand React and its syntax.
We recommend that you check out the tic-tac-toe game before continuing
with the tutorial. One of the features that you’ll notice is that there is a
numbered list to the right of the game’s board. This list gives you a history of
all of the moves that have occurred in the game, and it is updated as the game
progresses.
You can close the tic-tac-toe game once you’re familiar with it. We’ll be
starting from a simpler template in this tutorial. Our next step is to set you up
so that you can start building the game.
Prerequisites
We’ll assume that you have some familiarity with HTML and JavaScript, but
you should be able to follow along even if you’re coming from a different
programming language. We’ll also assume that you’re familiar with
programming concepts like functions, objects, arrays, and to a lesser extent,
classes.
If you need to review JavaScript, we recommend reading this guide. Note that
we’re also using some features from ES6 — a recent version of JavaScript. In
this tutorial, we’re using arrow functions, classes, let, and const statements.
You can use the Babel REPL to check what ES6 code compiles to.
Overview
Now that you’re set up, let’s get an overview of React!
What Is React?
React is a declarative, efficient, and flexible JavaScript library for building user
interfaces. It lets you compose complex UIs from small and isolated pieces of
code called “components”.
React has a few different kinds of components, but we’ll start
with React.Component subclasses:
class ShoppingList extends React.Component {
render() {
return (
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
);
}
}
We’ll get to the funny XML-like tags soon. We use components to tell React
what we want to see on the screen. When our data changes, React will
efficiently update and re-render our components.
Here, ShoppingList is a React component class, or React component type. A
component takes in parameters, called props (short for “properties”), and
returns a hierarchy of views to display via the render method.
The render method returns a description of what you want to see on the screen.
React takes the description and displays the result. In particular, render returns
a React element, which is a lightweight description of what to render. Most
React developers use a special syntax called “JSX” which makes these
structures easier to write. The <div /> syntax is transformed at build time
to React.createElement('div'). The example above is equivalent to:
return React.createElement('div', {className: 'shopping-list'},
React.createElement('h1', /* ... h1 children ... */),
React.createElement('ul', /* ... ul children ... */)
);
This Starter Code is the base of what we’re building. We’ve provided the CSS
styling so that you only need to focus on learning React and programming the
tic-tac-toe game.
By inspecting the code, you’ll notice that we have three React components:
Square
Board
Game
The Square component renders a single <button> and the Board renders 9
squares. The Game component renders a board with placeholder values which
we’ll modify later. There are currently no interactive components.
Passing Data Through Props
To get our feet wet, let’s try passing some data from our Board component to
our Square component.
Before:
After: You should see a number in each square in the rendered output.
If you click on a Square now, you should see ‘click’ in your browser’s devtools
console.
Note
To save typing and avoid the confusing behavior of this, we will use the arrow
function syntax for event handlers here and further below:
class Square extends React.Component {
render() {
return (
<button className="square" onClick={() => console.log('click')}>
{this.props.value}
</button>
);
}
}
Note
In JavaScript classes, you need to always call super when defining the
constructor of a subclass. All React component classes that have
a constructor should start with a super(props) call.
Now we’ll change the Square’s render method to display the current state’s
value when clicked:
Replace this.props.value with this.state.value inside the <button> tag.
Replace the onClick={...} event handler with onClick={() =>
this.setState({value: 'X'})}.
Put the className and onClick props on separate lines for better readability.
After these changes, the <button> tag that is returned by the
Square’s render method looks like this:
class Square extends React.Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
render() {
return (
<button
className="square" onClick={() => this.setState({value: 'X'})}
>
{this.state.value} </button>
);
}
}
The React DevTools let you check the props and the state of your React
components.
After installing React DevTools, you can right-click on any element on the
page, click “Inspect” to open the developer tools, and the React tabs (“⚛️
Components” and “⚛️Profiler”) will appear as the last tabs to the right. Use “⚛️
Components” to inspect the component tree.
However, note there are a few extra steps to get it working with
CodePen:
4. In the new tab that opens, the devtools should now have a React tab.
Lifting state into a parent component is common when React components are
refactored — let’s take this opportunity to try it out.
Add a constructor to the Board and set the Board’s initial state to contain an
array of 9 nulls corresponding to the 9 squares:
class Board extends React.Component {
constructor(props) { super(props); this.state = { squares:
Array(9).fill(null), }; }
renderSquare(i) {
return <Square value={i} />;
}
Next, we need to change what happens when a Square is clicked. The Board
component now maintains which squares are filled. We need to create a way
for the Square to update the Board’s state. Since state is considered to be
private to a component that defines it, we cannot update the Board’s state
directly from Square.
Instead, we’ll pass down a function from the Board to the Square, and we’ll
have Square call that function when a square is clicked. We’ll change
the renderSquare method in Board to:
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)} />
);
}
Note
We split the returned element into multiple lines for readability, and added
parentheses so that JavaScript doesn’t insert a semicolon after return and
break our code.
Now we’re passing down two props from Board to Square: value and onClick.
The onClick prop is a function that Square can call when clicked. We’ll make
the following changes to Square:
Replace this.state.value with this.props.value in
Square’s render method
Replace this.setState() with this.props.onClick() in
Square’s render method
Delete the constructor from Square because Square no longer keeps track of
the game’s state
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</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>
);
}
}
After these changes, we’re again able to click on the Squares to fill them, the
same as we had before. However, now the state is stored in the Board
component instead of the individual Square components. When the Board’s
state changes, the Square components re-render automatically. Keeping the
state of all squares in the Board component will allow it to determine the
winner in the future.
Since the Square components no longer maintain state, the Square
components receive values from the Board component and inform the Board
component when they’re clicked. In React terms, the Square components are
now controlled components. The Board has full control over them.
Note how in handleClick, we call .slice() to create a copy of the squares array to
modify instead of modifying the existing array. We will explain why we create a
copy of the squares array in the next section.
Why Immutability Is Important
In the previous code example, we suggested that you use the .slice() method
to create a copy of the squares array to copy instead of modifying the existing
array. We’ll now discuss immutability and why immutability is important to
learn.
There are generally two approaches to changing data. The first approach is
to mutate the data by directly changing the data’s values. The second
approach is to replace the data with a new copy which has the desired
changes.
Data Change with Mutation
var player = {score: 1, name: 'Jeff'};
player.score = 2;
// Now player is {score: 2, name: 'Jeff'}
// Or if you are using object spread syntax proposal, you can write:
// var newPlayer = {...player, score: 2};
The end result is the same but by not mutating (or changing the underlying
data) directly, we gain several benefits described below.
Complex Features Become Simple
Taking Turns
We now need to fix an obvious defect in our tic-tac-toe game: the “O”s cannot
be marked on the board.
We’ll set the first move to be “X” by default. We can set this default by
modifying the initial state in our Board constructor:
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
xIsNext: true, };
}
With this change, “X”s and “O”s can take turns. Try it!
Let’s also change the “status” text in Board’s render so that it displays which
player has the next turn:
render() {
const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
return (
// the rest has not changed
After applying these changes, you should have this Board component:
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: Array(9).fill(null),
xIsNext: true, };
}
handleClick(i) {
const squares = this.state.squares.slice(); squares[i] =
this.state.xIsNext ? 'X' : 'O'; this.setState({ squares: squares,
xIsNext: !this.state.xIsNext, }); }
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>
);
}
render() {
const status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
return (
<div>
<div className="status">{status}</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>
);
}
}
Given an array of 9 squares, this function will check for a winner and
return 'X', 'O', or null as appropriate.
We will call calculateWinner(squares) in the Board’s render function to check if a
player has won. If a player has won, we can display text such as “Winner: X” or
“Winner: O”. We’ll replace the status declaration in Board’s render function with
this code:
render() {
const winner = calculateWinner(this.state.squares); let status; if
(winner) { status = 'Winner: ' + winner; } else { status = 'Next
player: ' + (this.state.xIsNext ? 'X' : 'O'); }
return (
// the rest has not changed
First, we’ll set up the initial state for the Game component within its
constructor:
class Game extends React.Component {
constructor(props) { super(props); this.state = { history: [{
squares: Array(9).fill(null), }], xIsNext: true, }; }
render() {
return (
<div className="game">
<div className="game-board">
<Board />
</div>
<div className="game-info">
<div>{/* status */}</div>
<ol>{/* TODO */}</ol>
</div>
</div>
);
}
}
renderSquare(i) {
return (
<Square
value={this.props.squares[i]} onClick={() =>
this.props.onClick(i)} />
);
}
render() {
const winner = calculateWinner(this.state.squares);
let status;
if (winner) {
status = 'Winner: ' + winner;
} else {
status = 'Next player: ' + (this.state.xIsNext ? 'X' : 'O');
}
return (
<div>
<div className="status">{status}</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>
);
}
}
Since the Game component is now rendering the game’s status, we can
remove the corresponding code from the Board’s render method. After
refactoring, the Board’s render function looks like this:
render() { return ( <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>
);
}
Note
Unlike the array push() method you might be more familiar with,
the concat() method doesn’t mutate the original array, so we prefer it.
At this point, the Board component only needs
the renderSquare and render methods. The game’s state and
the handleClick method should be in the Game component.
View the full code at this point
Showing the Past Moves
Since we are recording the tic-tac-toe game’s history, we can now display it to
the player as a list of past moves.
We learned earlier that React elements are first-class JavaScript objects; we
can pass them around in our applications. To render multiple items in React,
we can use an array of React elements.
In JavaScript, arrays have a map() method that is commonly used for mapping
data to other data, for example:
const numbers = [1, 2, 3];
const doubled = numbers.map(x => x * 2); // [2, 4, 6]
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClick={(i) => this.handleClick(i)}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol> </div>
</div>
);
}
to
<li>Ben: 9 tasks left</li>
<li>Claudia: 8 tasks left</li>
<li>Alexa: 5 tasks left</li>
In addition to the updated counts, a human reading this would probably say
that we swapped Alexa and Ben’s ordering and inserted Claudia between
Alexa and Ben. However, React is a computer program and does not know
what we intended. Because React cannot know our intentions, we need to
specify a key property for each list item to differentiate each list item from its
siblings. One option would be to use the strings alexa, ben, claudia. If we were
displaying data from a database, Alexa, Ben, and Claudia’s database IDs could
be used as keys.
<li key={user.id}>{user.name}: {user.taskCount} tasks left</li>
When a list is re-rendered, React takes each list item’s key and searches the
previous list’s items for a matching key. If the current list has a key that didn’t
exist before, React creates a component. If the current list is missing a key that
existed in the previous list, React destroys the previous component. If two keys
match, the corresponding component is moved. Keys tell React about the
identity of each component which allows React to maintain state between re-
renders. If a component’s key changes, the component will be destroyed and
re-created with a new state.
key is a special and reserved property in React (along with ref, a more
advanced feature). When an element is created, React extracts the key property
and stores the key directly on the returned element. Even though key may look
like it belongs in props, key cannot be referenced using this.props.key. React
automatically uses key to decide which components to update. A component
cannot inquire about its key.
It’s strongly recommended that you assign proper keys whenever you
build dynamic lists. If you don’t have an appropriate key, you may want to
consider restructuring your data so that you do.
If no key is specified, React will present a warning and use the array index as a
key by default. Using the array index as a key is problematic when trying to re-
order a list’s items or inserting/removing list items. Explicitly
passing key={i} silences the warning but has the same problems as array
indices and is not recommended in most cases.
Keys do not need to be globally unique; they only need to be unique between
components and their siblings.
Implementing Time Travel
In the tic-tac-toe game’s history, each past move has a unique ID associated
with it: it’s the sequential number of the move. The moves are never re-
ordered, deleted, or inserted in the middle, so it’s safe to use the move index
as a key.
In the Game component’s render method, we can add the key as <li
key={move}> and React’s warning about keys should disappear:
const moves = history.map((step, move) => {
const desc = move ?
'Go to move #' + move :
'Go to game start';
return (
<li key={move}> <button onClick={() =>
this.jumpTo(move)}>{desc}</button>
</li>
);
});
If we click on any step in the game’s history, the tic-tac-toe board should
immediately update to show what the board looked like after that step
occurred.
View the full code at this point
Wrapping Up
Congratulations! You’ve created a tic-tac-toe game that:
Nice work! We hope you now feel like you have a decent grasp of how React
works.
Check out the final result here: Final Result.
If you have extra time or want to practice your new React skills, here are some
ideas for improvements that you could make to the tic-tac-toe game which
are listed in order of increasing difficulty:
1. Display the location for each move in the format (col, row) in the move history
list.
3. Rewrite Board to use two loops to make the squares instead of hardcoding
them.
4. Add a toggle button that lets you sort the moves in either ascending or
descending order.
5. When someone wins, highlight the three squares that caused the win.
6. When no one wins, display a message about the result being a draw.
Throughout this tutorial, we touched on React concepts including elements,
components, props, and state. For a more detailed explanation of each of
these topics, check out the rest of the documentation. To learn more about
defining components, check out the React.Component API reference.