React first steps

Last year I had the opportunity of working on a VueJS project. It was my first experience using a modern Front-end framework. Nowadays, I'm working with a legacy codebase written in the good old Backbone.js, but I'm still eager to keep digging into new stuff, so I've decided to try React.

Don't get me wrong; I enjoyed VueJS a lot, and I plan to keep using it in the future if it fits my needs, but I'm curious about all the buzz around React, the massive adoption it has, the love and hate stories about JSX, CSS in JS et al.

I'm not trying to compare both frameworks, that's a very well covered topic. Instead, I'm trying to look at React with a complete noob view. That's why I'm following the "official" tutorial (interactive tic-tac-toe game) and taking notes about the code snippets that feel unfamiliar, adding comments on the code itself, and asking questions that I hope to answer at some point.

Overview

The ShoppingList component: nomenclature is pretty basic. Since code examples use ES6 class syntax, I prefer the term React component class than React component type. At this point, I don't see any difference between them.

class ShoppingList extends React.Component {
// `render` method returns a "React element": a lightweight description of
// what to render. JSX syntax gets transformed at build time to
// `React.createElement` calls.
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>
);
}
}

Things I notice:

Inspecting the Starter Code

Passing props is how information flows in React apps, from parents to children.

A pattern I'm already familiar with.

Making an Interactive Component

I can't help it, but inline event handlers feel wrong to me. The tutorial gives you advice about using arrow functions for these in order to avoid confusion on the this behavior.

render() {
return (
<button
className="square"
onClick={() => this.setState({value: 'X'})}>
{this.state.value}
</button>
);
}

By calling this.setState from an onClick handler in the Square’s render method, we tell React to re-render that Square whenever its <button> is clicked.

I was expecting an explicit call to a render method, but I guess this is all about "reactivity", and all the magic would be lost if it didn't work that way. What if a child component gets a new state, does its parent get re-rendered?

About JSX comments: syntax error will be thrown if placed wrong, it seems they have to be inside the components tree.

  Line 102:12:  Parsing error: Unexpected token, expected ","

100 | return (
101 | {/* TODO */}
> 102 | <div className="game">
| ^

Lifting State Up

... state is considered to be private to a component that defines it...

So, child components shouldn't change parent's state directly (parent to child flow again). Now the Square component gets an event handler as a "prop" from its parent and also displays a value instead of keeping its own state.

class Square extends React.Component {
render() {
return (
<button
className="square"
onClick={() => this.props.onClick()}>
{this.props.value}
</button>
);
}
}

Now, as expected, the Board component will have an onClick "prop" that gets called whenever the <button> is clicked.

// Board
handleClick(i) {
const squares = this.state.squares.slice();
squares[i] = 'X';
this.setState({squares: squares});
}
renderSquare(i) {
return (
<Square
value={this.state.squares[i]}
onClick={() => this.handleClick(i)}
/>
);
}

At this point, it's a little bit confusing because of the multiple onClick occurrences in such a small code snippet. According to the docs, it seems that React sets up a "native" click event handler when an onClick attribute is defined for regular HTML elements, but not for custom components. So, the hyphotetical vanilla JS counterpart will look something like this:

<button onclick="() => parentComponent.handleChildButtonClick()">

Thus, the onClick attribute (or prop) on the <Square> definition could have been defined as something more specific, i.e. onAction, onPlay, etc. Note: stick to React's conventions: on[Event] and handle[Event] method naming.

This requires to be very careful when defining components hierarchy. The key is to wisely decide which component is responsible for the state. The tutorial introduces a new term for child components that don't handle state: controlled component. I don't find it particularly useful; it's just one more concept to wrap your head around.

Why Immutability Is Important

The main benefit of immutability is that it helps you build pure components in React.

By now, there're three different concepts that I'm not able to fully distinguish. These are my own definitions that could be completely wrong:

On a complex component hierarchy, it seems helpful to use "function component" referring to those that don't handle state. I'm still getting used to the class syntax, so being able to write these as function declarations is nice.

// React function component: notice the lack of `this` references
// The tutorial warns about this change, but doesn't give further details.
function Square(props) {
return (
<button className="square" onClick={props.onClick}>
{props.value}
</button>
);
}

Showing the Past Moves

Being able to render an array of components looks cool, but each component needs to have a unique key prop for React to keep track of all the items in the array between different render steps. Some notes about this:

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 not explicitly set, the default value for key would be the array index. The tutorial warns you about not using these defaults. It uses an example that mentions working with a database: displaying a list of users and using {user.id} as key. It sounds reasonable, but I won't be using a database most of the time. At this point, I wonder if there's any React API method which generates unique identifiers, if so, why it isn't the default option; would I have to use something like _.uniqueId from underscore or lodash?

In the next section, it resolves to use the array index with a textual description as key prefix, which feels just the opposite of the previous advice. However, it states to be a safe option because the list is never re-ordered.

When adding the jumpTo method, I run into a parsing error. I'm used to writing commas after method definitions; I keep forgetting you don't need them inside the body of a class.

Tutorial completed. Let's dig deeper by doing the exercises:

1. Display the location for each move in the format (col, row) in the move history list.

Calculating the row and column of a play having its index seems easy. Division for the row (i / 3) + 1 (quotient), and modulo operation for the column (i + 1) mod 3 (remainder). Adding up 1 in each case to avoid displaying zeroes.

What isn't straight forward is to get that index in the render method, though. I spent half an hour trying to make it work without setting that index in the state. It turns out, that's the easiest solution, to add a currentPlayIndex to the state on the Square click handler whenever a play is performed.

Setting the index of the current play:

handleClick(i) {
//...
this.setState({
history: history.concat([{
squares: squares,
currentPlayIndex: i,
}]),
stepNumber: history.length,
xIsNext: !this.state.xIsNext,
});
}

Render method changes to display row and column values:

const moves = history.map((step, move) => {      
let pos = ''
if (step.playIndex !== null) {
const quotient = Math.floor(step.playIndex / 3);
const remainder = (step.playIndex + 1) % 3;

const row = quotient + 1;
const col = remainder === 0 ? 3 : remainder;

pos = `(${row}, ${col})`;
}
const desc = move ?
`Go to move #${move}` :
'Go to game start';
return (
<li key={desc}>
<button onClick={() => this.jumpTo(move)}>{desc}</button> {pos}
</li>
);
});

2. Bold the currently selected item in the move list

Although game improvements are listed in order of increasing difficulty, in my opinion this one is easier than the previous one. It's a matter of defining a className for the list item when this.state.stepNumber === move, and then create a new style rule. Let's say .is-active { font-weight: bold }, so:

const className = this.state.stepNumber === move ? 'is-active' : ''
return (
<li key={desc} className={className}>
<button onClick={() => this.jumpTo(move)}>{desc}</button> {pos}
</li>
);

A quick search on stackoverflow ("React dynamic class names") reveals that the module classnames utility is very popular, even the official docs recommend it.

3. Rewrite the board to use two loops to make the squares instead of hardcoding then

The section "Lists and Keys" is the main reference in the docs regarding the use of loops. They favor map above everything else, so let's do it their way:

render() {
const rows = Array.from(Array(3).keys())
const cols = Array.from(Array(3).keys())
return (
<div>
{rows.map((row) =>
<div key={`board-row-${row}`} className="board-row">
{cols.map((col) =>
const index = row === 0 ? col : (row * cols.length) + col;
return this.renderSquare(index)
)}
</div>
)}
</div>
);
}

Notes:

At this point I should re-visit the 1st improvement to remove hardcoded magic numbers.

4. Add a toggle button that lets you sort the moves in either ascending or descending order.

This is about to flip the history depending on the selected order, just before mapping the moves. To keep it simple, we add a direction property to the state, and then we check that value on the Game's render method.

First, add the default direction on the Game's constructor:

this.state = {
history: [{
squares: Array(9).fill(null),
playIndex: null,
}],
stepNumber: 0,
xIsNext: true,
direction: 1, // as in SQL: ASC -> 1, DESC -> -1
};

Then, we add the toggle button and define the event handler:

<button onClick={() => this.flipMoves()}>flip moves</button>

The event handler is super simple, just change the direction whenever the button is clicked:

flipMoves() {
this.setState({ direction: this.state.direction * -1 })
}

Note: my initial option has been to use an arrow function for the event handler in order to keep the this reference pointing to the game instance. However, the docs make good advice against this pattern because it could lead to performance issues (see Handling events).

Now, check direction before mapping the moves; if direction is DESC, just draw the moves backwards:

// reverse history if necessary
if (this.state.direction < 0) {
history.reverse()
}

const moves = history.map((step, move) => {
// let's flip the current move index depending on direction
move = this.state.direction < 0 ? (this.state.stepNumber - 1) - move : move
//...

Note: we can safely use Array.prototype.reverse() because history is a fresh copy, so current state won't be mutated.

OK, it works, but given the fact we use an ordered list to show de movements, it would be nice to be able to display the list indexes in the correct order. I started by searching on MDN for a CSS property that would allow me to get it, something like list-style-type: reverse. After a couple of searches, I came up with the HTML reversed attribute.

<ol reversed={this.state.direction < 0}>{moves}</ol>

5. When someone wins, highlight the three squares that caused the win

For this one, I'm using the same approach as I'd use for number 2. It's about adding a .is-winner class name to those squares belonging to the winner play, and then creating some style rule to highlight them.

Step by step:

5.1. Change calculateWinner helper to return the sequence of winning moves (which is already calculated) along with the player.

function calculateWinner(squares) {
//...
return {
player: squares[a],
sequence: lines[i]
}
// ...
}

5.2. Pass a new prop from Game to Board when rendering it. Since winner could be null, let the Board component to handle it, and make sure the object contains a sequence array.

5.3. Now, on the Board component check if current square to be drawn is part of the winner sequence, if so, apply the .is-winner class name.

class Board extends React.Component {
renderSquare(i) {
const isWinnerPlay = (this.props.winner &&
this.props.winner.sequence &&
this.props.winner.sequence.indexOf(i) >= 0) ? 'is-winner' : ''
return (
<Square
key={`square-${i}`}
value={this.props.squares[i]}
onClick={() => this.props.onClick(i)}
className={isWinnerPlay}
/>
);
}
//...
}

5.4. Make sure that the Square component gets the class name from its parent:

function Square(props) {
return (
<button className={`square ${props.className}`} onClick={props.onClick}>
{props.value}
</button>
);
}

Note: specifying a className works for regular DOM elements. So if you want to pass a class name from a parent component to a child one, the latter will get a className prop. It seems this is a very common pattern:

// parent
render() {
const classNameFromParent = 'foo'
return (<Child className={classNameFromParent} />);
}

// child, see how class name from parent gets appended to `bar`
render() {
return (<p className={`bar ${this.props.className}`}>hello world</p>);
}

Done. After completing the improvements, I don't feel a much deeper understanding than before, but I have to admit it's been fun. Now, I hope to re-visit this in some weeks or months to check if I've made any progress.

Gist with the complete solution