« Back to home page

React Tutorial (2017) — The Beginner's Guide to the React JavaScript Library

Introduction

Getting Started

Components, Part 1

At their most basic level, every React project is a collection of components.

What is a component?

A component is a part of your application’s interface.

For example, imagine creating a blog interface with React. (This is something you can easily do with a tool like Gatsby.)

If this were the case, we might create a component named “BlogPost”, and this component would contain all of the code related to how a blog post both looks and behaves. This component would include the code for the structure of the post, such as the h1 tags for the heading and perhaps a div element to hold the content of the blog post; the code for the styling of the post, which basically means the CSS associated with the post; and the code for how the post behaves, meaning any code that makes the post interactive, such as a button that allows you to mark a post as read.

The benefit of creating this “BlogPost” component is that, ideally, all of the styling and the behavior of this component will be self-contained. This means, even if we’re working on an entirely separate component, there is very little chance that we’ll accidentally break anything about the look or functionality of the “BlogPost” component, and if something does break, we’ll be able to easily track down the problem.

But the real benefit of splitting an interface into components is felt once you start building an interface with more complexity.

For example, imagine if we wanted two components for blog posts: one that is used to display the full post and one that is only used to display the summary.

These components serve different purposes and will appear in different places, but there would still be a lot of redundant code between them.

To account for this, we could break the “BlogPost” component down into a series of smaller, reusable components. We might, for instance, create the following components:

Then we might include the “Headline”, “Byline”, and “Content” components inside a “BlogPostComplete” component, and include the “Headline”, “Byline”, and “Summary” components inside a “BlogPostSummary” component.

This would mean, if we ever wanted to change the way the headline looks or behaves, or the way the byline looks and behaves, we would only have to make those changes in a single place.

Even in an interface of modest complexity, this ability to reuse parts of the interface in different contexts greatly reduces the complexity of the code.

If you’ve built any kind of web application before, or even a WordPress theme, you might think that components sound a lot like partial templates – meaning, templates that exist only to be included inside other templates. If you want to think of components like this, that’s fine. Just know that components allow us to do a lot more than partial templates. To understand what “a lot more” means though, we’re going to have to start writing some code.

Create a Component

In this section, we’re going to create our first React component. To do this though, we’re going to need to do a (tiny) bit of setup.

To begin, create a file named “index.js” inside the project’s “src” directory. By the end of this book, the code for our interface will be split across a number of files, but for the sake of simplicity, we’re going to start by placing all of the code inside the “index.js” file.

Inside this file, we need access to the React library.

If we were embedding the library into a HTML page, we’d use this code:

<script src="https://unpkg.com/react@15/dist/react.js"></script>

But we’re not embedding the library into a HTML page. The library itself has been defined a dependency for this project using the “package.json” file, and now we need to make sure the “index.js” knows about that dependency.

To do this, we could use a require statement:

var React = require('react');

Then we could reference the library by referring to the “React” variable.

But these days, with the ES6 syntax, we use the import keyword:

import React from 'react'

This achieves the same thing but is the preferred approach.

Note: We don’t have to use a capital letter for “React”, and we could name this variable whatever we want, but what we’ve done here is the standard stylistic choice.

Beneath the import statement, create a function named “App”:

function App(){
    // code goes here
}

This function is our first React component. It might not look like much, but that’s a good thing. There’s a lot of weird things about React but, when it comes to the syntax, we’re mostly going to be writing regular JavaScript, so any JavaScript experience you already have will carryover tremendously. In fact, in a well-designed React project, the majority of your components will be completely unremarkable JavaScript functions. The more advanced types of components, which we’ll cover later on, will be few and far between.

In terms of how we’ve named the function, there’s a couple of things you should know:

#1 – React components are always named in Pascal Case

This means the name of React components always start with a capital letter, and any subsequent words in the name are also capitalized. For example, if we created a component to hold a player’s name, we might name it “PlayerName”.

#2 – The top-level component in a project should be called “App”

This isn’t a strict requirement, but it’s a well-accepted convention. In most cases, components should be named quite descriptively, but since the purpose of the “App” component will to be eventually act as a container for the rest of the components in the project, using the “App” name makes a lot of sense.

To make the “App” component do something, we can use a return statement, just as we would with any other JavaScript function. In fact, we have to use a return statement. There’s no other way for our component to be of use.

What should the component return?

Because a component is a part of an interface, the component should return a part of an interface.

To do this, return a h1 element:

function App(){
    return <h1>Leaderboard</h1>;
}

If this looks weird, that’s fair enough.

Here, we’re returning what appears to be HTML from within our JavaScript code. In actual fact though, this is not HTML. Instead, this is JSX – a syntax that was designed by the developers of React to simplify the process of creating interfaces with React. JSX is extremely similar to HTML, but there are some key differences that we’ll explore throughout this book.

It’s worth noting, however, that we don’t have to use JSX. In reality, JSX is described as “syntactic sugar”. This means it only exists to make our lives easier. Without JSX, the we could recreate the previous code snippet by using the createElement method, available to us in standard JavaScript:

function App(){
    return React.createElement('h1', {}, 'Leaderboard');
}

But the benefit of JSX is that it’s easier to read and write than regular JavaScript, which means it’s more pleasant to work with while reducing the chance of errors. For these reasons, JSX is the officially recommended syntax for designing React interfaces and it’s what we’ll be using throughout this book. Ultimately though, JSX is compiled into regular JavaScript, and this will be useful to know once we start encountering some of its quirks.

To end this section, wrap the JSX code inside a pair of parentheses:

function App(){
    return (<h1>Leaderboard</h1>);
}

…and put the JSX on a separate line:

function App(){
    return (
        <h1>Leaderboard</h1>
    );
}

This isn’t strictly necessary, but once we start working with larger chunks of JSX, the parentheses will act as a useful separator to distinguish between the JSX and the JavaScript, so most React developers choose to include them.

Mounting Components

In the previous section, we created our first component, but by default, this component won’t appear inside the web browser.

Why?

The important thing to understand is that a React component can take over an entire web page – meaning, your entire website can be built with React – or it can simply be used for part of a web page. You could, for instance, create a blog with WordPress and insert a React component into the sidebar. (People don’t generally mix WordPress and React, but it is technically possible.)

Because of this choice, React doesn’t assume that we want our web page to be taken over by React, and as a result, we need to manually tell React where we want it to be placed within a web page.

This process is known as mounting a component.

To mount the “App” component, start by creating an “index.html” file inside the project’s “public” directory.

Then define a basic HTML structure inside this file:

<!DOCTYPE html>
<html>
    <head>
        <title>Leaderboard</title>
    </head>
    <body>
    </body>
</html>

This file is now the home page of our React application, and it should now appear within the web browser.

Inside this file, create a div element that we can attach the “App” component to:

<!DOCTYPE html>
<html>
    <head>
        <title>Leaderboard</title>
    </head>
    <body>
        <div id="root"></div>
    </body>
</html>

Here, we’ve created this div element with an id of “root”. It doesn’t matter what id you associate with this element, but “root” is a typical choice.

Next, switch back to the “index.js” file and add a second import statement:

import React from 'react'
import ReactDOM from 'react-dom'

Here, we’re importing this “ReactDOM” package. This is equivalent to embedding the “react-dom.js” file we talked about earlier:

<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>

The reason we have a “React” package and a “ReactDOM” package is because React can be used to build interfaces on other platforms, such as mobile devices, so the library has been split into multiple parts: there’s the core React library in the “React” package and anything specific to web development is inside the “ReactDOM” package. As such, we need to import both packages.

At the bottom of the “index.js” file, define a ReactDOM.render method:

ReactDOM.render();

This method comes from the “ReactDOM” package, and it allows us to attach a component to an element within a web page.

The method accepts two arguments:

  1. What we want to mount
  2. Where we want to mount it

The important thing to note about this method is that we can’t just pass through the name of a component as the first argument. We need to pass through JSX code.

For example, here’s one way we could use this method:

ReactDOM.render(<h1>This is JSX</h1>, document.getElementById("root"));

Here, we’re passing this h1 element into the methods as a first argument – and remember, this is JSX, not HTML – and then we’re using getElementById to tell ReactDOM where we want to put that element. In this case, we’re saying: “Put the h1 element inside the element with an id of ‘root’.”

You’ll notice that, because of this code, the “This is JSX” code appears inside the browser.

But we don’t want to pass all of our JSX code into this ReactDOM.render method. Instead, we want to pass through a component – specifically, the “App” component that we’ve already defined.

To do this, change the statement to the following:

ReactDOM.render(<App />, document.getElementById("root"));

Here, we’ve replaced the previous JSX code with what appears to be a self-closing HTML tag. This is the JSX syntax for including a React component. It’s a self-closing tag with the name of the component inside the tag, and it’s a syntax we’ll be using many times throughout this book.

Because of this change, the content of the “App” component will now appears inside the web browser.

You might be wondering though:

How is the JavaScript code we’ve written affecting the “index.html” file that we’ve created? Because the “index.html” file doesn’t use any script tags. As far as we can see, there is no connecting at all between these files.

But the answer is quite simple: create-react-app takes care of it for us. It automatically injects the script tags into the “index.html” file during both the development and the build process, so we don’t have to think about it.

It’s also worth noting that, while we could attach a number of completely separate React components to the web page we’ve created, the most common practice is to create a single, top-level component – such as this “App” component – and then include every other component from the project inside this component. As a result, the majority of React projects will only use a single ReactDOM.render method, and once this method has been setup, it’s unlikely that you’ll ever need to come back to it. It’s very much a “set it and forget it” kind of deal.

Adjacent Elements

As I mentioned before, JSX has a handful of quirks that we need to be mindful of. To see one of these quirks, add a p element to the “App” component:

function App(){
    return (
        <h1>Leaderboard</h1>
        <p>Welcome to Leaderboard!</p>
    );
}

This seems like an unremarkable addition, and yet, if you check the command line or the JavaScript Console, you’ll notice the following error:

Adjacent XJS elements must be wrapped in an enclosing tag

What’s going on?

Behind the scenes, this JSX code is trying to compile into regular JavaScript, but in its current form, it’s trying to compile into code that returns two createElement methods:

function App(){
    return React.createElement('h1', {}, 'Leaderboard');
    return React.createElement('p', {}, 'Welcome to Leaderboard!');
}

But a JavaScript function can’t return more than one value, which is why we end up with this error. The JSX is not properly formatted.

To fix this error, wrap the h1 and p elements inside a div element:

function App(){
    return (
        <div>
            <h1>Leaderboard</h1>
            <p>Welcome to Leaderboard!</p>
        </div>
    );
}

This solves the problem because now the “App” function only returns a single createElement method – in this case, the createElement method for the div element – and the other createElement methods for the h1 and p elements are nested inside that div element.

The lesson here is that, while we can create complex interfaces inside React components, we always need to make sure that only one element is being returned. This can simply mean wrapping all of a component’s elements inside a single element. In this case, we’ve used a div element as that wrapper, but there’s no reason that we have to use a div element.

We could, for instance, use a span element:

function App(){
    return (
        <span>
            <h1>Leaderboard</h1>
            <p>Welcome to Leaderboard!</p>
        </span>
    );
}

…or a section element:

function App(){
    return (
        <section>
            <h1>Leaderboard</h1>
            <p>Welcome to Leaderboard!</p>
        </section>
    );
}

This is one of those quirks that continues to be a stumbling block for many beginning React developers though, so don’t worry if you continue to encounter the “Adjacent elements” error. You’ll pick up the new habit soon enough.

Thinking in React

React is weird. Beyond the JSX syntax, React encourages a lot of practices that, at least when it launched, were considered quite strange. The syntax of React is mostly familiar – again, we’ll mostly be writing regular JavaScript – but many of the ideas we’ll encounter are unconventional.

Because of these oddities, the developers of React put together a page in the official documentation called “Thinking in React”, which provides people with the overall, birds-eye view of building an interface with React.

All up, there are six steps involved in creating React interfaces:

  1. Start with a mock
  2. Break the user interface into a component hierarchy
  3. Build a static version in react
  4. Identify the minimal (but complete) presentation of user interface state
  5. Identify where your state should live
  6. Add inverse data flow

If some (or all) of these steps sound like impenetrable gibberish, that’s fine. I’m not going to assume that you have any idea of what “state” is, or what “inverse data flow” might be. Everything will be explained.

What is useful, however, is if we use these six steps as a basic outline for putting together the Leaderboard application. That way, you won’t just reach the final page of this book knowing how to write React code. You’ll know how to design a React project from scratch.

Creating a Mock

The first step in creating any kind of interface – whether or not you’re working with React – is to figure out what that interface will look like before you start writing any code.

In general, I prefer to sketch prototypes with pencil and paper, but since this isn’t a book about user interface design, we’re going to jump straight into writing HTML code (which we’ll then convert to JSX).

At the start of the design process, I like to use the “Epicenter Design” method that’s described in the book Getting Real:

Epicenter design focuses on the true essence of the page — the epicenter — and then builds outward. This means that, at the start, you ignore the extremities: the navigation/tabs, footer, colors, sidebar, logo, etc. Instead, you start at the epicenter and design the most important piece of content first.

Basically, we want to ask ourselves: “What is the essence of the Leaderboard application? What must exist for the rest of the application be of use?”

In this case, what must exist is a list of players. There needs to be a list of players, and each player needs both a name and a score. Without a list of players, the application can’t possibly do anything else of use.

With this in mind, here’s how we might create such a list in HTML:

<ul>
    <li>David: 0</li>
    <li>Bob: 0</li>
    <li>Mary: 0</li>
    <li>Tim: 0</li>
    <li>Warren: 0</li>
    <li>Bill: 0</li>
</ul>

For this list to be interactive though, we’ll need to be able to select the players and change their scores. To allow for this, create a couple of buttons that will be used to affect the score of the selected player:

<ul>
    <li>David: 0</li>
    <li>Bob: 0</li>
    <li>Mary: 0</li>
    <li>Tim: 0</li>
    <li>Warren: 0</li>
    <li>Bill: 0</li>
</ul>
<div>
    <button>Give 5 Points</button>
    <button>Take 5 Points</button>
</div>

Then create a form that allows players to add players to the list:

<form>
    <input type="text">
    <button type="submit">Add Player</button>
</form>
<ul>
    <li>David: 0</li>
    <li>Bob: 0</li>
    <li>Mary: 0</li>
    <li>Tim: 0</li>
    <li>Warren: 0</li>
    <li>Bill: 0</li>
</ul>
<div>
    <button>Give 5 Points</button>
    <button>Take 5 Points</button>
</div>

The next step is to place this HTML into the “App” component. Then we can turn this mock into something interesting and functional. Before we can place the HTML into a component though, we need to convert it to JSX. This, fortunately, only requires a small change, since all we have to do is ensure that the input element is self-closing:

<form>
    <input type="text" />
    <button type="submit">Add Player</button>
</form>

This is because when writing JSX, all elements must be closed. If an element has an opening and starting tag – such as <div> and </div> – then both must be used. Otherwise, the tag must be self-closing.

If you forget to close a tag, you’ll receive an error.

After making this change, replace the JSX inside the “App” component with the JSX that we’ve just written:

function App(){
    return (
        <div>
            <h1>Leaderboard</h1>
            <form>
                <input type="text" />
                <button type="submit">Add Player</button>
            </form>
            <ul>
                <li>David: 0</li>
                <li>Bob: 0</li>
                <li>Mary: 0</li>
                <li>Tim: 0</li>
                <li>Warren: 0</li>
                <li>Bill: 0</li>
            </ul>
            <div>
                <button>Give 5 Points</button>
                <button>Take 5 Points</button>
            </div>
        </div>
    );
}

This isn’t the most remarkable interface in the world, but it’s a good enough starting point that we’ll expand upon in the coming chapters.

Multiple Components

It’s possible for us to create an entire React interface inside the “App” component. But since this component is simply a function, it’s not difficult to imagine why it’d be silly to place that much code in one place:

We’d end up with a function that is a confusing and bloated mess.

What we want to do is split our interface into a number of smaller components. This is the second step of the “Thinking in React” process, which involves breaking the user interface down into a component hierarchy.

Ultimately, the ideal is that each component will be so small and so specific that it will adhere to the “single responsibility principle”, which is to say: every component in our project will only have a single responsibility.

That is to say, every component will only do one thing.

At the moment, the “App” component does not have a single responsibility. It’s actually doing everything, which means it’s responsible for our entire application. Here, for instance, are some things it’s currently doing:

This is a problem because, as soon as the interface is interactive, the fact that this component is doing everything will make it difficult to maintain.

If, on the other hand, we split this component into a series of much smaller components, each operating as its own, independent piece of the interface, we can change those individual pieces without breaking the rest of the interface.

We’ll also be able to:

  1. Reuse our components in different parts of the project
  2. Reuse our components in entirely separate projects

It’s difficult to feel the full extent of these benefits until you’re building moderately complex interfaces, and at first, it might even seem like we’re doing a whole lot of extra work for no real reason, but stick with it. By the end of the book, the benefits of this process will be obvious.

With all of this said, we have to ask ourselves:

What is the primary purpose of the “App” component? What is the one thing it should be responsible for?

As I mentioned earlier, the primary purpose of the “App” component should be to act as a container for the rest of the interface. In practice, this means we’re going to split the “App” component into a series of smaller components and then the “App” component is only going to include those components. As a result, the other components will be doing all of the actual work.

To implement this new structure, we’re going to create three components:

We could break this application into even smaller components, and that is something we’ll do later in the book, but for the time being, it’s fine if we’re a little more conservative in how we approach this.

Inside the “index.js” file, create the “AddPlayerForm” component by creating a new function and returning the relevant JSX code from the “App” component:

function AddPlayerForm(){
    return (
        <form>
            <input type="text" />
            <button type="submit">Add Player</button>
        </form>
    );
}

Then repeat the process for the “PlayerList” component:

function PlayerList(){
    return (
        <ul>
            <li>David: 0</li>
            <li>Bob: 0</li>
            <li>Mary: 0</li>
            <li>Tim: 0</li>
            <li>Warren: 0</li>
            <li>Bill: 0</li>
        </ul>
    );
}

…and the “PlayerControls” component:

function PlayerControls(){
    return (
        <div>
            <button>Give 5 Points</button>
            <button>Take 5 Points</button>
        </div>
    );
}

It doesn’t strictly matter where you place these functions in the “index.js” file. They can be placed before or after the “App” component.

Next, remove everything from the “App” component except for the div element:

function App(){
    return (
        <div>
        </div>
    );
}

Then include the “AddPlayerForm” component inside the “App” component:

function App(){
    return (
        <div>
            <AddPlayerForm />
        </div>
    );
}

Here, we’re using the same syntax that we used with the ReactDOM.render method. We’ve placed the name of the component inside a self-closing tag, which is how we include one component inside another component.

Repeat this process for the “PlayerList” and “PlayerControls” components:

function App(){
    return (
        <div>
            <AddPlayerForm />
            <PlayerList />
            <PlayerControls />
        </div>
    );
}

Here, we’ve removed the h1 element from the “App” component. You can leave this element where it was, or alternatively place it inside the “index.html” file:

<!DOCTYPE html>
<html>
    <head>
        <title>Leaderboard</title>
    </head>
    <body>
        <h1>Leaderboard</h1>
        <div id="root"></div>
    </body>
</html>

The downside of this approach is that we can’t write any React code to affect the h1 element, since it’s not inside a React component, but we weren’t going to do that anyway, so there’s no practical downside.

Based on the code we’ve written, the interface will look like it did before, but its structured in a much more modular way.

Nesting Components

At this point, we’re still in the second step of the “Thinking in React” process, which involves breaking the user interface down into a component hierarchy. We’re not going to break everything down into the smallest possible component just yet, but I would like to take things one step further.

Start by taking a look at the “PlayerList” component:

function PlayerList(){
    return (
        <ul>
            <li>David: 0</li>
            <li>Bob: 0</li>
            <li>Mary: 0</li>
            <li>Tim: 0</li>
            <li>Warren: 0</li>
            <li>Bill: 0</li>
        </ul>
    );
}

Here, we can see a considerable amount of repetition. We have this list of li elements, all with an identical structure:

<li>NAME: SCORE</li>

The data is different inside each li element, but when we see this sort of repetition of structure in a React project, that’s the exact scenario where we can use components to greatly reduce the amount of code we have to maintain.

What we want to do is create a component that we can use to replace reach li element inside the “PlayerList” component. That component will define the structure of each player’s li element, which means if we want to change the structure of the li element, we only have to make the change in one place.

To achieve this, create a component named “PlayerListItem” component:

function PlayerListItem(){
    // code goes here
}

This component should return an li element with a player’s name and score:

function PlayerListItem(){
    return (
        <li>David: 0</li>
    );
}

Here, we’re hard-coding the name “David” and the score “0” into the component itself, but soon, we’re going to dynamically change these values, so this one component will become reusable for each of the players in the list.

Back in the “PlayerList” component, include this “PlayerListItem” component a handful of times, replacing each of the li elements:

function PlayerList(){
    return (
        <ul>
            <PlayerListItem />
            <PlayerListItem />
            <PlayerListItem />
            <PlayerListItem />
            <PlayerListItem />
            <PlayerListItem />
        </ul>
    );
}

Here, we’ve manually included the “PlayerListItem” component six times inside the “PlayerList” component. This doesn’t really fix the problem of repetitive code – especially since we now we have a list of players named “David” – but this is a step in the right direction. We’ll see why in the next section.

Props

The purpose of any interface is to display – and allow users to interact with – data. In this case, for instance, we want to display the data of players in a leaderboard.

Because this is such a fundamental part of building an interface, React allows us to pass data into components. This is a huge part of what allows components to be reused throughout a project. We can use the same component in different places but change what it displays – and how it behaves – based on what we pass into it.

When we pass data into a component, that data is known as “props”. This is short-hand for “properties”, but since “props” is the official terminology, that’s how I’ll refer to it from this point onward.

Passing Props

Passing props into a component is a lot like defining attributes on a HTML element. For example, if we created an input element in HTML, we might give that element a name attribute:

<input type="text" name="myInputField" />

In the same way, we can define a “name” prop for the “PlayerListItem” component:

function PlayerList(){
    return (
        <ul>
            <PlayerListItem name="David" />
            <PlayerListItem name="Bob" />
            <PlayerListItem name="Mary" />
            <PlayerListItem name="Tim" />
            <PlayerListItem name="Warren" />
            <PlayerListItem name="Bill" />
        </ul>
    );
}

The important thing to note, however, is that we don’t have to predefine a component’s props. This “name” prop, for instance, is completely arbitrary. We could change it to “myCoolProp” if we wanted to:

function PlayerList(){
    return (
        <ul>
            <PlayerListItem myCoolProp="David" />
            <PlayerListItem myCoolProp="Bob" />
            <PlayerListItem myCoolProp="Mary" />
            <PlayerListItem myCoolProp="Tim" />
            <PlayerListItem myCoolProp="Warren" />
            <PlayerListItem myCoolProp="Bill" />
        </ul>
    );
}

…or any other name.

You can name a component’s props however you like and you can define as many or as few of them as you like. You can also pass any type of data into a component via a prop. In this case, we’re passing a string, but we can also pass numbers, JavaScript objects, and even functions.

But passing data into a component is only half the battle. The other half of the battle is allowing the component to display (and use) that data. This is known as receiving props.

Receiving Props

Inside the “PlayerListItem” component, place the word “props” between the parentheses of the function:

function PlayerListItem(props){
    return (
        <li>David: 0</li>
    );
}

By adding the “props” parameter to the component, we can now access the props being passed into this component by referencing this parameter.

To see this in action, add a console.log statement to the component:

function PlayerListItem(props){
    console.log(props);
    return (
        <li>David: 0</li>
    );
}

You’ll see how, for each of the players in the list, an object is output to the JavaScript Console. This object is the “props” object. Whenever we pass props into a component, this object becomes available to us. There’s nothing special about it, either. It’s just a standard JavaScript object that holds whatever data has been passed into the component. In this case, for instance, it contains the “name” prop that we defined a moment ago.

…and because this “props” object is a regular JavaScript object, we can access the values of individual keys from that object using dot-notation. In this case, for instance, we can access the value of the “name” key:

function PlayerListItem(props){
    console.log(props.name);
    return (
        <li>David: 0</li>
    );
}

Another way to see the props associated with a component is to open the React DevTools and select a component that has props being passed into it.

You’ll see the “props” object in the sidebar of the DevTools.

Displaying Props

In this section, we want to display the value of the “name” prop from inside the “PlayerListItem” component.

To do this, we can a wrap a pair of curly braces around a reference to “props.name” from inside the return statement:

function PlayerListItem(props){
    return (
        <li>{props.name}: 0</li>
    );
}

The reason this works is because, when working with JSX, a pair of curly braces allows us to execute JavaScript expressions from inside the return statement.

For example, we could write mathematical expressions within the curly braces:

function PlayerListItem(props){
    return (
        <li>{1 + 1}: 0</li>
    );
}

Or, more usefully, store “props.name” inside a “name” variable, and then reference that variable from inside the JSX code:

function PlayerListItem(props){
    const name = props.name;
    return (
        <li>{name}: 0</li>
    );
}

It is possible to write much more complicated JavaScript between these curly braces, and some people prefer that approach, but for the most part, we’re going to write the bulk of our JavaScript outside of the return statement and only use curly braces for relatively simple expressions.

Next, pass a “score” prop into the “PlayerListItem” component:

function PlayerList(){
    return (
        <ul>
            <PlayerListItem name="David" score="0" />
            <PlayerListItem name="Bob" score="0" />
            <PlayerListItem name="Mary" score="0" />
            <PlayerListItem name="Tim" score="0" />
            <PlayerListItem name="Warren" score="0" />
            <PlayerListItem name="Bill" score="0" />
        </ul>
    );
}

Then create a “score” variable inside the “PlayerListItem” component that holds the value of “props.score”, and reference this from inside the JSX:

function PlayerListItem(props){
    const name = props.name;
    const score = props.score;
    return (
        <li>{name}: {score}</li>
    );
}

There is, however, a problem with this “score” prop:

At the moment, we’re passing through the prop as a string, but this is going to become a problem later on when we want to modify the scores with the “Give 5 Points” and “Take 5 Points” button.

What we really is for the “score” prop to be an integer.

The seemingly straight-forward approach is to use the parseInt function:

function PlayerListItem(props){
    const name = props.name;
    const score = parseInt(props.score);
    return (
        <li>{name}: {score}</li>
    );
}

But there is an easier, more direct approach.

All we have to do is, when passing the “score” values into the component, wrap them in a pair of curly braces instead of quotation marks:

function PlayerList(){
    return (
        <ul>
            <PlayerListItem name="David" score={0} />
            <PlayerListItem name="Bob" score={0} />
            <PlayerListItem name="Mary" score={0} />
            <PlayerListItem name="Tim" score={0} />
            <PlayerListItem name="Warren" score={0} />
            <PlayerListItem name="Bill" score={0} />
        </ul>
    );
}

This ensures that the “score” value is passed in as an integer, rather than a string.

Based on the changes we’ve made, the interface won’t look any different than it did before, and there’s still plenty of repetition, but the code is looking cleaner and we’re continuing to move in the right direction.

Listing Components

At the moment, we’re manually creating each of the “PlayerListItem” components:

<PlayerListItem name="David" score={0} />
<PlayerListItem name="Bob" score={0} />
<PlayerListItem name="Mary" score={0} />
<PlayerListItem name="Tim" score={0} />
<PlayerListItem name="Warren" score={0} />
<PlayerListItem name="Bill" score={0} />

This is both repetitive and impractical though, as all of the data associated with the players is hard-coded into the component’s return statement.

The ideal approach would be to create an array that holds all of the names and scores of the players in the list, and to then use that array to generate this list of components. That would reduce the repetition while also separating the data from the rest of the code, which makes the code easier to work with.

To achieve this, create an array named “playerData” inside the “PlayerList” component:

function PlayerList(){
    const playerData = [];
    return (
        <ul>
            <PlayerListItem name="David" score={0} />
            <PlayerListItem name="Bob" score={0} />
            <PlayerListItem name="Mary" score={0} />
            <PlayerListItem name="Tim" score={0} />
            <PlayerListItem name="Warren" score={0} />
            <PlayerListItem name="Bill" score={0} />
        </ul>
    );
}

Inside this array, each player will be represented by an object, and each object will have a “name” and a “score” property;

Here’s how we can define a player named “David” with a score of “0”:

const playerData = [
    { name: "David", score: 0 }
];

Then it’s just a matter of adding the rest of the players to the array, ensuring that each of the objects are separated by a comma:

const playerData = [
    { name: "David", score: 0 },
    { name: "Bob", score: 0 },
    { name: "Mary", score: 0 },
    { name: "Tim", score: 0 },
    { name: "Warren", score: 0 },
    { name: "Bill", score: 0 }
];

In context, this is what the code looks like:

function PlayerList(){
    const playerData = [
        { name: "David", score: 0 },
        { name: "Bob", score: 0 },
        { name: "Mary", score: 0 },
        { name: "Tim", score: 0 },
        { name: "Warren", score: 0 },
        { name: "Bill", score: 0 }
    ];
    return (
        <ul>
            <PlayerListItem name="David" score={0} />
            <PlayerListItem name="Bob" score={0} />
            <PlayerListItem name="Mary" score={0} />
            <PlayerListItem name="Tim" score={0} />
            <PlayerListItem name="Warren" score={0} />
            <PlayerListItem name="Bill" score={0} />
        </ul>
    );
}

Beneath the “playerData” array, create a forEach loop that loops through each of the objects in this array:

playerData.forEach(function(){
    console.log("Hello world");
});

The words “Hello world” should appear in the JavaScript Console six times, once for each object in the “playerData” array.

To access the data about each of the players within this loop, define a “player” parameter between the parentheses of the function, and output the value of this parameter with a console.log statement:

playerData.forEach(function(player){
    console.log(player);
});

You’ll see each of the player’s objects appear in the JavaScript Console.

Inside this loop, return the “name” property of the “player” object:

playerData.forEach(function(player){
    return player.name;
});

…and store this loop inside a “listOfPlayers” variable:

const listOfPlayers = playerData.forEach(function(player){
    return player.name;
});

Then convert the forEach function into a map function;

const listOfPlayers = playerData.map(function(player){
    return player.name;
});

If you’re not familiar with the map function, what it does is loop through an array, like the forEach function, but unlike the forEach function, it creates a new array based on whatever data is returned by the function.

To see this in action, output the value of the “listOfPlayers” variable:

const listOfPlayers = playerData.map(function(player){
    return player.name;
});
console.log(listOfPlayers);

You’ll see that, because of the map function, we’ve created this new array that only contains a list of the player’s names, which we extracted from the “playerData” array.

To see why this is significant, remove all of the “PlayerListItem” components from the “PlayerList” component’s return statement:

return (
    <ul></ul>
);

Then, within the ul element, reference the “listOfPlayers” variable:

return (
    <ul>
        {listOfPlayers}
    </ul>
);

Because of this code, a list of the player’s names will appearing inside the interface, rather than a list of the “PlayerListItem” components.

This isn’t exactly what we want, but it allows us to do something cool.

Inside the map function, return a block of JSX:

const listOfPlayers = playerData.map(function(player){
    return ();
});

Then, inside this block, reference the “PlayerListItem” component:

const listOfPlayers = playerData.map(function(player){
    return (
        <PlayerListItem />
    );
});

…and pass “player.name” and “player.score” into the component:

const listOfPlayers = playerData.map(function(player){
    return (
        <PlayerListItem name={player.name} score={player.score} />
    );
});

Because of this change, the “listOfPlayers” array will now contain all of the “PlayerListItem” components that, earlier, we’d created by hand. As a result, these components will now be dynamically inserted into the interface based on the contents of the array.

Visually, the interface won’t look any different from what we created before, but now there is vastly less repetition in the “PlayerList” component:

function PlayerList(){
    const playerData = [
        { name: "David", score: 0 },
        { name: "Bob", score: 0 },
        { name: "Mary", score: 0 },
        { name: "Tim", score: 0 },
        { name: "Warren", score: 0 },
        { name: "Bill", score: 0 }
    ];
    const listOfPlayers = playerData.map(function(player){
        return (
            <PlayerListItem name={player.name} score={player.score} />
        );
    });
    return (
        <ul>
            {listOfPlayers}
        </ul>
    );
}

This is because, instead of manually creating our list of components, we’ve taken a data source – in this case, the “playerData” array – and used this data source to automatically generate a list of components.

There is, however, a problem.

You will notice that, with this code in place, an error appears within the JavaScript Console:

Warning: Each child in an array or iterator should have a unique “key” prop. Check the render method of PlayerList.

This is something we’ll solve in the next section.

Keys

Whenever we tell React to generate a list of components, as we’ve done here, React needs a way to distinguish between those components. It needs to be able to recognize that this component is separate from that component.

This is because, as we’ll see later on, React is able to update certain parts of the interface when data changes, but sometimes React needs a helping hand in figuring out which part of the interface should be updated.

If React doesn’t know exactly what needs to be updated, it’ll update more than it needs to, which ends up being inefficient and slow.

To solve this problem, we can associate a key with each of the components that we’re generating via the map function.

A key is a unique ID that is passed into a component as a prop.

To see how we use keys, add an “index” parameter between the parentheses of the map function:

const listOfPlayers = playerData.map(function(player, index){
    return (
        <PlayerListItem name={player.name} score={player.score} />
    );
});

Then define a prop named “key” on the “PlayerListItem” component that uses the value of “index” as the value of the prop:

const listOfPlayers = playerData.map(function(player, index){
    return (
        <PlayerListItem key={index} name={player.name} score={player.score} />
    );
});

With this code in place, each component will have a unique ID based on its position in the “listOfPlayers” array, and the error will no longer appear inside the JavaScript Console.

The problem with this approach is that the “index” parameter can change. If we add or remove data from the “playerData” array, the “index” parameter will change and React will have to recalculate the “key” prop for each of the components. This is inefficient and not recommended by React’s developers.

Instead, what we want is for the “key” property to be a value that is guaranteed to be unique and that never changes.

In a real-world applications, it’s likely that whatever data you’re pulling into your project has an ID associated with it. The simple solution, then, is to use that ID as your key.

In this project, the “playerData” array is our data source, so the easiest way to solve our problem is to add an “id” property to each player object:

const playerData = [
    { id: 1, name: "David", score: 0 },
    { id: 2, name: "Bob", score: 0 },
    { id: 3, name: "Mary", score: 0 },
    { id: 4, name: "Tim", score: 0 },
    { id: 5, name: "Warren", score: 0 },
    { id: 6, name: "Bill", score: 0 }
];

It doesn’t matter what these “id” properties contain – they don’t even have to be numbers – as long as they’re unique. You also don’t have to call the property “id”. All that matters is that we have access to some property that contains unique values. If, for instance, we could guarantee that the “name” or “score” properties were unique, we could use those values as the “key”, but since they’re not guaranteed to be unique, it makes sense to create a separate “id” property.

Next, remove the “index” parameter from the map function and replace it with a reference to this “id” property:

const listOfPlayers = playerData.map(function(player){
    return (
        <PlayerListItem
            key={player.id}
            name={player.name}
            score={player.score} />
    );
});

Here, I’ve also moved the component’s props onto separate lines. This is a common convention for the sake of keeping props easy to read.

Based on this change, the interface won’t look any different, but once we start allowing React to automatically update parts of our interface, it will have a much easier time of figuring out what needs to be updated.

It’s worth noting, however, that we only need to add a “key” property to a component if that component is dynamically generated in a list.

We don’t, for instance, need to add a “key” property to the components that are included inside the “App” component:

function App(){
    return (
        <div>
            <AddPlayerForm key={1} />
            <PlayerList key={2} />
            <PlayerControls key={3} />
        </div>
    );
}

Fortunately, React will always let you know if you’ve forgotten to define a “key” property, so it’s always obvious when one is necessary.

Interactivity

At this point, we have a static version of the Leaderboard interface. It has most of the interface elements that will be in the final version but none of those elements actually do anything.

To remedy this, think back to the idea of “Epicenter Design” and ask yourself, “What must exist for the rest of the software to function as desired?”

Personally, I think that users need to be able to select a player on the list. If they can’t select a player on the list, they can’t affect the player’s scores with the “Give 5 Points” or “Take 5 Points” button, which means there’s no real point to the application existing in the first place.

You could argue that it’s just as important for users to be able to add and remove players from the list, but since that part of the process is a little trickier, it’s best if we focus on being able to select players.

In terms of what the “selecting a player” feature will look like, it’s quite basic:

  1. A user clicks anywhere within the bounds of a player’s li element to select that particular player.
  2. A class is applied to that li element, and this class changes the style of that li element.

In this case, the background color of the selected player will change to the color yellow, but you could any kind of styling to the selected player.

But while this feature is quite basic, it does require that we cover a range of topics before we can start putting together the puzzle pieces, so in this chapter, we’re only going to lay the foundations.

The rest of the feature will be created throughout subsequent chapters.

Triggering Events

In this section, we’re going to allow users to click players in the list and have that player’s name and score appear in the JavaScript Console. Later on, this functionality to determine which player the user has selected.

To begin, return to the “PlayerListItem” component:

function PlayerListItem(props){
    const name = props.name;
    const score = props.score;
    return (
        <li>{name}: {score}</li>
    );
}

Then inside this component, create a function named “handleClick”, beneath the variables and above the return statement:

function PlayerListItem(props){
    const name = props.name;
    const score = props.score;
    function handleClick(){ };
    return (
        <li>{name}: {score}</li>
    );
}

Inside this function, we’re going to write whatever code we want to run when a user clicks on one of the players. For the time being, use a console.log statement to output a string of “You clicked a player”:

function handleClick(){
    console.log("You clicked a player");
};

It’s worth noting that we don’t have to name this function “handleClick”. We could name it whatever we want. But since it’s common for React developers to write functions that do things when an event occurs, it’s common to use the word “handle” at the start of a function name to distinguish these functions from functions that aren’t triggered by an event.

To trigger the code inside this function when a player is clicked, add an onClick handler to the li element:

function PlayerListItem(props){
    const name = props.name;
    const score = props.score;
    function handleClick(){
        console.log("You clicked a player");
    };
    return (
        <li onClick={handleClick}>{name}: {score}</li>
    );
}

Here, we’ve passed the name of the function to the onClick handler, and as a result, users can now click a player to see the “You clicked a player” message appear in the JavaScript Console.

To output a player’s details from inside the console.log statement, it’s just a matter of referring to the “name” and “score” variables:

function PlayerListItem(props){
    const name = props.name;
    const score = props.score;
    function handleClick(){
        console.log(`You clicked on ${name}, who has a score of ${score}.`);
    };
    return (
        <li onClick={handleClick}>{name}: {score}</li>
    );
}

This will output a much more useful message to the JavaScript Console.

Selecting Players

In this section, we’re going to create a static, hard-coded version of the feature that will eventually allow users to select players on the list.

To begin, we’re going to create a JavaScript object that will hold the ID of a player. This is what object will look like:

{
    selectedPlayer: 3
}

Here, we’ve defined this “selectedPlayer” property and we’ve set this property to a value of “3”. In this case, “3” is the ID of a player in the “playerData” array. The specific value doesn’t matter. It should simply match the ID of any of the players in the “playerData” array.

From there, we’re going to:

  1. Pass this data from the “PlayerList” component, into the “PlayerListItem” component.
  2. Use this data to determine which li element in the list should have a class attached to them.

So, if we pass a value of “3” into the “selectedPlayer” property, the li element of the player with an ID of “3” will have a class attached to it, and that class will change the background color of the element to “yellow”.

To be clear, this functionality won’t be connected to the “handleClick” function that we created in the previous section. Eventually, users will be able to click a player to select them, but for the time being, all we’re going to allow is for us, as the developer, to manually define the selected player.

As with all of the examples in this book though, this is much easier to understand if you follow along by writing out the code.

Inside the “PlayerList” component, create an object named “data” that contains the “selectedPlayer” property that we just discussed:

function PlayerList(){
    const data = {
        selectedPlayer: 3
    }
    const playerData = [
        { id: 1, name: "David", score: 0 },
        { id: 2, name: "Bob", score: 0 },
        { id: 3, name: "Mary", score: 0 },
        { id: 4, name: "Tim", score: 0 },
        { id: 5, name: "Warren", score: 0 },
        { id: 6, name: "Bill", score: 0 }
    ];
    const listOfPlayers = playerData.map(function(player){
        return (
            <PlayerListItem
                id={player.id}
                name={player.name}
                score={player.score} />
        );
    });
    return (
        <ul>
            {listOfPlayers}
        </ul>
    );
}

Then pass this data into the “PlayerListItem” component:

<PlayerListItem
    key={player.id}
    name={player.name}
    score={player.score}
    selectedPlayer={data.selectedPlayer} />

Here, we’ve created this “selectedPlayer” prop, and we’re passing through a value of “data.selectedPlayer”, which refers to the “selectedPlayer” property of the object we just defined.

We’ll need to access the ID of the player – not just the ID of the selected player – from inside the “PlayerListItem” component, so pass this value into the component with a prop named “id”:

<PlayerListItem
    key={player.id}
    id={player.id}
    name={player.name}
    score={player.score}
    selectedPlayer={data.selectedPlayer} />

This “id” prop does seem redundant, as we’re already passing through the ID of the player via the “key” prop, but we need the “id” prop because React doesn’t allow us to access the “key” prop from inside the component. The “key” prop is only used to help React efficiently render components. If we want to access the ID of a player for our own purposes, we need to pass it in separately.

Next, return to the “PlayerListItem” component and create two variables inside the render method: one variable that contains the player’s ID and one that contains the selected player’s ID:

function PlayerListItem(props){
    const id = props.id;
    const name = props.name;
    const score = props.score;
    const selectedPlayer = props.selectedPlayer;
    function handleClick(){
        console.log(`You clicked on ${name}, who has a score of ${score}.`);
    };
    return (
        <li onClick={handleClick}>{name}: {score}</li>
    );
}

At this point, we want to implement the following logic:

If the value of the “id” variable is equal to the “selectedPlayer” variable, attach a class named “selected” to the component’s li element. If the value of “id” is not equal to “selectedPlayer”, do nothing.

With this code in place, we’ll be able to create a class named “selected” in a CSS file and use that class to define how a selected player’s li element should look.

To implement this logic, add the following code above the return statement:

let selectedClass;
if(id === selectedPlayer){
    selectedClass = "selected";
}

There are, however, more efficient ways of writing this. We could, for instance, achieve the same thing with a ternary operator:

const selectedClass = (id === selectedPlayer) ? "selected" : null;

There’s also React-specific tools that we can use to apply styles to elements based on specific conditions, and we’ll look at those later on.

Inside the “PlayerListItem” component’s return statement, add a class attribute to the li element and pass through the “selectedClass” variable we’ve created:

return (
    <li class={selectedClass} onClick={handleClick}>{name}: {score}</li>
);

But this code actually causes an error.

What’s going on?

This is another one of those JSX quirks.

The problem here is that we’ve defined this class attribute, but in JavaScript, class is a reserved word. As a result, when the JSX is compiled into regular JavaScript, the use of this class keyword is a problem. It’s not a keyword we can use when writing regular JavaScript – unless we’re specifically wanting to define a class – so it’s not a keyword we can use within our JSX.

The solution, fortunately, is simple enough.

Instead of using the class attribute, use the className attribute:

return (
    <li className={selectedClass} onClick={handleClick}>{name}: {score}</li>
);

In JSX, this className attribute is identical to the class attribute found in HTML.

With this code in place, right click on the player with the ID of “3” and select the “Inspect Element” option.

You’ll see that this player – and only this player – has a class of “selected” attached to their li element.

But if we change the value of the “selectedPlayer” property inside the “PlayerList” component’s “data” object to “5”:

const data = {
    selectedPlayer: 5
}

Then it’s the player with the ID of “5” that will have the “selected” class attached to their li element.

This isn’t the most remarkable feature in the world – especially since we have to edit the code to select a different player – but it’s still edging us forward, in the right in the direction.

Importing Stylesheets

There’s not much point in attaching the “selected” class to any of the li elements if we’re not going to define what that class should look like.

But how do we define styles in a React project?

If we wanted to keep things insanely simple, we could place inline styles inside the “index.html” file:

<!DOCTYPE html>
<html>
    <head>
        <title>Leaderboard</title>
        <style type="text/css">
            .selected{
                background-color: yellow;
            }
        </style>
    </head>
    <body>
        <h1>Leaderboard</h1>
        <div id="root"></div>
    </body>
</html>

But this wouldn’t be sustainable for most projects.

We could also create a stylesheet file inside the public directory and reference that using a link tag inside the “index.html” file, but that’s still a fairly limiting option.

For the time being, the best option is to create a stylesheet file inside the src directory and then import that into the “index.js” file.

To see how this works, create a file named “index.css” inside the src directory and place the following style inside of it:

.selected{
    background-color: yellow;
}

Then switch back to the “index.js” file and add the following line amongst the rest of the import statements at the top of the file:

import './index.css'

Here, we’re using the import keyword and specifying the location of the “index.css” file that we just specified. The “./” part before the file name is how we specify that the file is in the same directory as the “index.js” file.

With this code in place, the li element of the currently selected player will have a yellow background.

But how are we importing a stylesheet into a JavaScript file?

This is possible because of create-react-app – and specifically, Webpack. It’s not something we can do with regular JavaScript but it’s something that we have access to as long as we’re using these tools.

There’s a couple of benefits to this approach:

  1. Any stylesheets inside the src directory will be bundled together into a single file, which will make your React project load faster.
  2. Once we split our components into separate files, we could have a separate stylesheet for each component, making it easier to manage our styles.

There are, however, other ways of managing styles within React, and we’ll take a look at another popular method later in this book.

Components, Part 2

In React, there are two types of components:

So far, we’ve been creating functional components. These are the simplest types of components. They allow us to write JavaScript functions that return JSX code that is then rendered inside the browser.

If we want to do anything more complex than this though, we need to start creating class components. These are not vastly different to functional components – they still render JSX code inside the browser, for instance – but they will allow us to make our components significantly more powerful.

To become familiar with the basics of class components, we’re going to start by converting the “PlayerListItem” component into a class component.

At the moment, this is what the “PlayerListItem” component looks like:

function PlayerListItem(props){
    const id = props.id;
    const name = props.name;
    const score = props.score;
    const selectedPlayer = props.selectedPlayer;
    function handleClick(){
        console.log(`You clicked on ${name}, who has a score of ${score}.`);
    };
    const selectedClass = (id === selectedPlayer) ? "selected" : null;
    return (
        <li className={selectedClass} onClick={handleClick}>{name}: {score}</li>
    );
}

But before we continue, comment out this code. A lot of what we’ve learned about functional components will apply to class components, but there’s enough differences between them that it’s easier if we start from scratch.

Class Components

Inside the “index.js” file, create a class named “PlayerListItem”:

class PlayerListItem{
    // code goes here
}

This is a standard JavaScript class, which we’re able to create since the adoption of the ES6 syntax. If we wanted to, we could use a different syntax that works with ES5, but creating class components with ES5 is extremely different to creating class components with ES6, so we’re only going to talk about the ES6 syntax, which is what React’s developers recommend.

To allow this class to do React-specific things, we need to tell this class to extend React.Component. This is possible with the extends keyword:

class PlayerListItem extends React.Component{
    // code goes here
}

If you’re not familiar with the ES6 syntax, this might look a little weird, so let’s take a moment to understand what’s going on.

At the top of the “index.js” file, we’re importing the React library:

import React from 'react'

…and we’re using “React” as the variable that then allows us to refer to this library throughout our code.

Inside this library, there’s a whole lot of code to power React’s features, and a good chunk of that code powers a class named “Component”. This is the class that defines what a component is and how a component should behave.

When we’re defining functional components, we don’t have to think about this, since our functional components are just regular functions. These functional components don’t really do anything React-specific, aside from returning JSX, but the compilation from JSX into JavaScript is handled by Webpack, not React.

When we’re defining class components though, it’s because we want to access React-specific features, and it’s not enough to import React, define a class, and call it a day. There are situations, after all, where you might want to create a class that isn’t a component.

With this in mind, when we create a class that we want to be a component, we need to tell React that this class should inherit from the Component class inside the “React” library. This is what extends allows us to do.

Fortunately, we don’t have to think about any of this when we’re developing with React. As long as you remember to use the extends keyword whenever you create a class component, you’ll be good to go.

Rendering JSX

Inside a JavaScript class, we can create methods to define what the class can do. We could, for instance, define a “helloWorld” method that outputs a string to the JavaScript Console:

class PlayerListItem extends React.Component{
    helloWorld(){
        console.log("Hello world...")
    }
}

But while we’ll talk about custom methods later on, what we need to talk about first is the method that React expects to find in every class component.

It’s known as the render method.

Inside the “PlayerListItem” class, create a method named “render”:

class PlayerListItem extends React.Component{
    render(){
        /* Code goes here */
    }
}

It’s within this method that we define what the component should output to the browser.

For example, try returning a block of JSX:

class PlayerListItem extends React.Component{
    render(){
        return (
            <p>This is my class component!</p>
        );
    }
}

That text will then appear inside the interface.

The important thing to realize about the render method is that what we’ve created here is identical to the following functional component:

function PlayerListItem(){
    return (
        <p>This is my class component!</p>
    );
}

As such, if you ever find yourself with a React component that only has a single render method and no other functionality, you can simplify that code by converting it into a functional component. In fact, it’s actually a best practice to ensure that most of your components are functional components.

Why?

Because if the entire purpose of a component is to simply render something, that component is significantly easier to manage, test, and maintain.

You do, of course, need to create more complex components sooner or later, but the ideal is that those complex components are few and far between, as that is a sign of a well-structured and easily maintained project.

To end this section, move the code from inside the “PlayerListItem” functional component into the class component:

class PlayerListItem extends React.Component{
    render(){
        const id = props.id;
        const name = props.name;
        const score = props.score;
        const selectedPlayer = props.selectedPlayer;
        function handleClick(){
            console.log(`You clicked on ${name}, who has a score of ${score}.`);
        };
        const selectedClass = (id === selectedPlayer) ? "selected" : null;
        return (
            <li className={selectedClass} onClick={handleClick}>{name}: {score}</li>
        );
    }
}

But, since we’re going to have to handle them a little differently, get rid of the “handleClick” function and the onClick handler:

class PlayerListItem extends React.Component{
    render(){
        const id = props.id;
        const name = props.name;
        const score = props.score;
        const selectedPlayer = props.selectedPlayer;
        const selectedClass = (id === selectedPlayer) ? "selected" : null;
        return (
            <li className={selectedClass}>{name}: {score}</li>
        );
    }
}

You’ll notice that the following error appears in the JavaScript Console:

ReferenceError: props is not defined

As we can see here, there appears to be a problem with “props”.

This is because, inside a class component, we can’t reference a prop by writing “props.name” or “props.score”.

Instead, we have to write “this.props.name” and “this.props.score”:

class PlayerListItem extends React.Component{
    render(){
        const id = this.props.id;
        const name = this.props.name;
        const score = this.props.score;
        const selectedPlayer = this.props.selectedPlayer;
        const selectedClass = (id === selectedPlayer) ? "selected" : null;
        return (
            <li className={selectedClass}>{name}: {score}</li>
        );
    }
}

This change will fix the error, but it’s worth mentioning that, as you become familiar with class components, you’ll battle with many of these small and somewhat forgettable details. As such, if you ever encounter an error that isn’t particularly clear, ask yourself: “Am I trying to do something in a class component that can only be done inside a functional component?”

Fortunately, React is pretty good at spitting out easily understood – or at least, easily Google-able – error messages, so there’s rarely much distance between coming across and then conquering these sort of stumbling blocks.

Binding Methods

In this section, we’re going to create a “handleClick” method based on the “handleClick” function that we created earlier. This seems like it should be fairly straightforward process, but there is a quirk we have to account for.

Inside the “PlayerListItem” component, create a “handleClick” method that executes a console.log statement:

class PlayerListItem extends React.Component{
    handleClick(){
        console.log("You clicked a player");
    }
    render(){
        const id = this.props.id;
        const name = this.props.name;
        const score = this.props.score;
        const selectedPlayer = this.props.selectedPlayer;
        const selectedClass = (id === selectedPlayer) ? "selected" : null;
        return (
            <li className={selectedClass}>{name}: {score}</li>
        );
    }
}

Then add the onClick handler back to the li element:

return (
    <li className={selectedClass}
        onClick={handleClick}>
        {name}: {score}
    </li>
);

Here, I’ve put the attributes of the li element on separate lines, as I find that it helps readability.

If we test this code by clicking on a player though, an error will appear in the JavaScript Console.

Why?

As was the case with the props, because this is a class component, we need to add “this” in front of the reference to “handleClick”:

return (
    <li className={selectedClass}
        onClick={this.handleClick}>
        {name}: {score}
    </li>
);

If you click a player now, the message should appear as expected.

Next, we want to recreate the functionality we had before, where the name and the score of the player are output by the console.log statement:

handleClick(){
    console.log(`You clicked on ${this.props.name}, who has a score of ${this.props.score}.`);
}

You’ll notice that we haven’t been able to use the “name” and “score” variables that we defined in the render method, since the “handleClick” method doesn’t have access to these variables. (We could, of course, create local versions of these variables inside the method if we wanted to.)

You’ll also notice that, if you click a player, the following error appears inside the JavaScript Console:

TypeError: Cannot read property ‘props’ of null

Once again, there appears to be a problem with our props. This time around though, this problem seems particularly strange. Why we can access “this.props.name” and “this.props.score” inside the render method but not inside the handleClick method?

To understand what’s happening, add the following console.log statement to both the render and “handleClick” methods:

console.log(this);

(Temporarily comment out the console.log statement that is already inside the “handleClick” method to avoid errors.)

You’ll notice that, when this code is run inside the render method, this outputs each of the individual “PlayerListItem” components to the JavaScript Console. You’ll also see how each of the components has a “props” object.

But if you click any of these components, thereby outputting the value of this from inside the “handleClick” method, the only thing output is null.

This is why we can’t reference “this.props.name” from inside the “handleClick” method – because there is no this.

To fix this problem, we need to explicitly tell the “handleClick” method what this should be equal to, and in this case, we want this to be equal to the component itself.

If this sounds confusing, fear not. When working with JavaScript, the nature of this is always a pain in the butt. Your best bet is to just follow along by writing out the code. I’ll explain what you need to do and when you need to do it, and you can worry about the why and how later on.

Inside the render method, add a bind method to the end of “this.handleClick”:

return (
    <li className={selectedClass}
        onClick={this.handleClick.bind()}>
        {name}: {score}
    </li>
);

…and pass through a value of “this”:

return (
    <li className={selectedClass}
        onClick={this.handleClick.bind(this)}>
        {name}: {score}
    </li>
);

Like I said, it doesn’t strictly matter if you don’t understand the mechanics behind what we’re doing here, but I’ll try my best to explain them:

Inside the render method, this is equal to the “PlayerListItem” component, but inside the “handleClick” method, this is equal to null. By using the bind method, we can transport the value of this from the render method into the “handleClick” method. As a result, this inside of the “handleClick” method will also be equal to the “PlayerListItem” component.

To see this effect, try clicking on any of the players in the list.

You’ll see that, with each click, the “PlayerListItem” component is output to the JavaScript Console.

Next, remove the newly created console.log statement from the “handleClick” method and uncommon the previous statement:

handleClick(){
    console.log(`You clicked on ${this.props.name}, who has a score of ${this.props.score}.`);
}

With this code in place, clicking on any of the players will now output the value of the clicked player’s “name” and “score” props, as desired.

It’s worth noting that there are multiple ways of implementing this bind functionality, some of which are preferable to what we’ve done here. We’ll explore some of these other options later, but the approach we’ve taken in this example is generally the most straight-forward.

For the time being, it’s mostly important to know when to use the bind method, and fortunately, the litmus test is fairly simple:

The bind method is used whenever you want to access this from inside a user-defined method.

You do not need to use the bind method if:

  1. The method does not need reference this in any way.
  2. The method is one of React’s built-in methods, such as the render method.

It’s also important to remember that the bind method is never used inside the user-defined method. Instead, the bind method is always attached to the code that calls the user-defined method.

The good news is, understanding the bind method is something that plenty of React developers struggle with, so if you’re having trouble wrapping your head around it, that’s absolutely fine. You’re perfectly capable of continuing onward. Struggling to grasp the bind method won’t prevent you from working your way through the rest of this book.

Lifecycle Methods

As we’ve already talked about before, every class component within a React project needs a render method. This render method has a specific purpose that is defined by the React library (unlike the “handleClick” method, which we’ve defined ourselves).

But the render method is not the only method that React treats differently from our own, custom methods.

There’s a number of other methods that we can add to our components to achieve specific goals, including:

These are known as lifecycle methods.

To see what these lifecycle methods allow us to do, add the componentWillMount and the componentDidMount methods to the “PlayerListItem” component:

class PlayerListItem extends React.Component{
    componentWillMount(){
        console.log("The component is about to mount!");
    }
    componentDidMount(){
        console.log("The component finished mounting!")
    }
    handleClick(){
        console.log(`You clicked on ${this.props.name}, who has a score of ${this.props.score}.`);
    }
    render(){
        const id = this.props.id;
        const name = this.props.name;
        const score = this.props.score;
        const selectedPlayer = this.props.selectedPlayer;
        const selectedClass = (id === selectedPlayer) ? "selected" : null;
        return (
            <li className={selectedClass}
                onClick={this.handleClick.bind(this)}>
                {name}: {score}
            </li>
        );
    }
}

You’ll see that, after the page refreshes, both of these statements appear in the JavaScript Console. What’s significant though is that the console.log statement inside the componentWillMount will always be executed before the console.log statement inside the componentDidMount method. Likewise, the code inside the compoenntDidMount method will always be executed before the

This is because lifecycle methods are designed to allow us to execute code at specific points of a component’s lifecycle.

For example, before a component is rendered in the browser via the render method, that component needs to be mounted. This is the moment in the component’s lifecycle where React figures out where the component should be inserted into the page. (If you recall, we mounted the “App” component inside the div element named “root” using the ReactDOM.render method.)

With this in mind, it’s common for React developers to use the componentDidMount method to load data from an external source. This ensures that the data has finished loading from that source right before the interface is rendered inside the browser.

But that’s only an example of how we might use lifecycle methods. For the time being, the only lifecycle method we need is the constructor method.

The constructor method is executed when a component is created, before every other method in that component.

It’s worth noting that, unlike the other lifecycle methods, the constructor method is not specific to React. It’s a standard part of the ES6 syntax. If you created a regular JavaScript class, you would be able to use the constructor method to ensure that certain code is executed whenever an instance of that class is created.

In the next chapter, we’ll see why this constructor method is useful in a React project, but let’s just focus on implementation for the time being.

Inside the “PlayerListItem” component, create a constructor method:

constructor(){
    console.log("The component is being created...")
}

Because this method will be the first thing that’s executed when the component is created, it makes sense to place it at the top of the component, above the rest of the methods.

If you save the file though, an error will appear in the JavaScript Console:

ReferenceError: this is not defined

This might seem a little odd, since we haven’t referenced this inside the constructor method, which must mean that the constructor method has broken the rest of the component – after all, we only reference this in the rest of the component’s methods.

To fix this problem, add the super keyword to the top of the method:

constructor(){
    super();
    console.log("The component is being created...")
}

As is the case with the constructor method, this super keyword is not specific to React – nor is it something we’ve defined ourselves. Instead, it’s a standard part of the ES6 syntax.

What does it do?

Think back to the fact that the React library has a class named “Component” that the “PlayerListItem” class is inheriting from, which is what allows the class to do React-specific things.

That’s what this line does:

class PlayerListItem extends React.Component{
    /* code goes here */
}

If we didn’t inherit from the “Component” class, the “PlayerListItem” class would be a regular JavaScript class and not a component.

What’s important to understand is that the “Component” class has its own constructor method that is executed whenever a class component is created, and that constructor needs to be executed for the component to be created successfully.

But if we define our own constructor method inside our component:

constructor(){
    console.log("The component is being created...")
}

…the constructor that we’ve defined will overwrite the constructor method from the “Component” class. As a result, the code that needs to run from inside the “Component” class will never run.

To solve this problem, we need to ensure that, even if we define our own constructor method, the constructor method inside the “Component” class is executed anyway – and before the constructor method we’ve created.

This is what the super keyword does for us:

constructor(){
    super();
    console.log("The component is being created...")
}

The super keyword looks for a method of the same name in the class that the current class inherits from, and executes that method.

In this case, the super keyword is used inside the constructor method, so when the “PlayerListItem” component is created, this code will look for the constructor method inside the “Component” class – the class that the “PlayerListItem” class inherits from – and execute that code.

As a result, if we include the super keyword in the constructor method, the error will no longer appear in the JavaScript Console.

You’ll also notice that the console.log statement from the constructor method appears first, before the console.log statements from the componentWillMount and componentDidMount methods. This proves that code inside the constructor method runs before the code in any other methods.

If you’re still not completely sure what’s going on with the super keyword though, that’s fine. The only thing you need to remember is that, if you create a constructor method inside a React component, you need to add the super keyword to the top of that method. As long as you remember that, your components will operate as expected.

To end this section, remove the constructor, componentWillMount, and componentDidMount methods from the “PlayerListItem” component. They won’t be necessary inside this particular component.

State

Throughout the previous chapters, we’ve covered a lot of ground, but it might not feel like we’ve achieved a whole lot. This is because the interface we’ve built is pretty darn uninteresting. It doesn’t do anything that we couldn’t do with regular JavaScript, making React feel like unnecessary complexity.

Fortunately, that’s about to change.

In this chapter, we’re going to (finally) allow users to select a player by clicking on them. This, in itself, is not the world’s greatest accomplishment, but it will allow us to talk about state, one of React’s central features.

By the end of this chapter, a lot of the ideas we’ve been discussing will start to come together.

Creating State

At the moment, the only way for us to select a player on the list is to manually change the value of the “selectedPlayer” property, which we’ve defined inside the “PlayerList” component:

const data = {
    selectedPlayer: 3
}

This, obviously, isn’t a convenient way to affect an interface.

To implement a better solution, we’re going to start working a feature of React known as state. The best way to understand state is by seeing it in action, so that’s what we’re going to do.

To begin, we need to convert the “PlayerList” component from a functional component, into a class component. This is because state can only be created and used inside a class component. (You’ll see why in a moment.)

After making this conversion, this is what the component should look like:

class PlayerList extends React.Component{
    render(){
        const data = {
            selectedPlayer: 3
        }
        const playerData = [
            { id: 1, name: "David", score: 0 },
            { id: 2, name: "Bob", score: 0 },
            { id: 3, name: "Mary", score: 0 },
            { id: 4, name: "Tim", score: 0 },
            { id: 5, name: "Warren", score: 0 },
            { id: 6, name: "Bill", score: 0 }
        ];
        const listOfPlayers = playerData.map(function(player){
            return (
                <PlayerListItem
                    key={player.id}
                    id={player.id}
                    name={player.name}
                    score={player.score}
                    selectedPlayer={data.selectedPlayer} />
            );
        });
        return (
            <ul>
                {listOfPlayers}
            </ul>
        );
    }
}

Here, we’ve used the class keyword to define the “PlayerList” class, used the extends keyword to inherit from “React.Component”, and wrapped all of the component’s code inside the render method.

Next, add a constructor method inside the “PlayerList” component:

constructor(){
    super();
}

Then, from inside this method, output the value of this:

constructor(){
    super();
    console.log(this);
}

You’ll see the “PlayerList” component appear in the JavaScript Console.

If you click on the downward-facing arrow of this component, you’ll see that is has a “props” object, which is the object that contains all of the props being passed into the component. In this case though, there aren’t any props being passed into the component, so the object is empty.

You’ll also notice that there’s a state object attached to the component, which is what we’re really interested in. This object is currently null, but let’s try setting its value from inside the constructor method.

To do this, set “this.state” equal to a JavaScript object:

constructor(){
    super();
    this.state = {
        selectedPlayer: 3
    }
    console.log(this);
}

Based on this change, the component’s state object will now be equal to the object we’ve defined here.

To see what’s significant about this, change the return statement inside the “PlayerList” component from this:

return (
    <ul>
        {listOfPlayers}
    </ul>
);

…to this:

return (
    <ul>
        {listOfPlayers}
        <li>Selected Player: {this.state.selectedPlayer}</li>
    </ul>
);

Here, we’ve added this extra li element to the bottom of the player’s list, and this li element contains a reference to “this.state.selectedPlayer,” which is how we refer to the “selectedPlayer” data that we just defined a moment ago inside the constructor method. You’ll notice that this is similar to how we retrieve props. We’ve just replaced the word “props” with “state”.

With this code in place, the value of “this.state.selectedPlayer” will appear within the interface, at the bottom of the player’s list.

But that’s not the significant part.

To see the significant part, switch back to the browser, open the React DevTools, and select the “PlayerList” component. You’ll see that, in the sidebar, there is a heading that says “State”, and beneath this heading is the “selectedPlayer” value we’ve defined in the constructor method.

If you don’t see the “selectedPlayer” value, make sure you’ve selected the correct component. State is always attached to a specific component, so if you’ve selected the wrong component, the state object will be empty.

Next, click on the value of “selectedPlayer” and try changing the value to the ID of a different player. You’ll notice that, as soon as you change the value, the value within the interface changes instantly, without a page refresh.

This is what makes state so significant.

What’s happening here is that, as we modify the state of the “PlayerList” component, React automatically figures out:

  1. What part of the state has changed.
  2. What part of the interface needs to change.

Then it automatically makes that change within the interface.

But, to be clear, React is not simply reloading the entire “PlayerLIst” component. That would be inefficient. Instead, React is smart enough to know how to update the interface in the smallest way possible. This is what allows React interfaces to feel incredibly responsive. It has a copy of the component before the state has changed and a copy of the component after the state has changed, and it intelligently merges the differences.

The truly brilliant thing, however, is that we don’t have to think about what React is doing. React will do all the heavy-lifting when it comes to figuring out how it should update the interface. All we have to do is tell React what data can be changed and where that data should be displayed. In this case, for instance, we know that users should be able to change the value of “selectedPlayer”. Therefore, we put that data in the component’s state object. If the value of “selectedPlayer” never needed to change, we wouldn’t put it into the state object.

Passing State

At the moment, the highlighted player in the list is highlighted based on the “selectedPlayer” value inside the “data” object:

const data = {
    selectedPlayer: 3
}

…rather than “selectedPlayer” value inside the state object:

this.state = {
    selectedPlayer: 3
}

But if we change this, we can make it so changing the value of “selectedPlayer” in the state object affects which player in the list has the “selected” class applied to their li element.

To do this, return to the map function:

const listOfPlayers = playerData.map(function(player){
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={data.selectedPlayer} />
    );
});

…and change “data.selectedPlayer” to “this.state.selectedPlayer”:

const listOfPlayers = playerData.map(function(player){
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={this.state.selectedPlayer} />
    );
});

Here, we’ve changed where the “selectedPlayer” data is coming from. Instead of it coming from the “data” object, it’s coming from the state object. But it’s still being passed into the “PlayerListItem” component in the same way, as a prop named “selectedPlaer”.

Unfortunately, this code will cause an error:

Uncaught TypeError: Cannot read property ‘state’ of undefined

To understand the problem, output the value of this from inside the map method:

const listOfPlayers = playerData.map(function(player){
    console.log(this);
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={this.state.selectedPlayer} />
    );
});

You’ll see that this is undefined.

What’s going on?

The problem is, the function that we’re passing into the map method is creating its own context. As a result, when we reference this from inside that function, this is not equal to the “PlayerList” component, which is what we want.

As we’ve talked about before, this can be a tricky and confusing beast to work with – especially if you’re new to JavaScript – but in this case, the solution is relatively straightforward.

Inside the map method, change the function into an ES6 arrow function;

const listOfPlayers = playerData.map((player) => {
    console.log(this);
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={this.state.selectedPlayer} />
    );
});

Here, we’ve removed the word function at the start of the function and placed an arrow – => – before the opening curly brace.

Based on this change, when we reference this from inside the map method, it will be equal to the “PlayerList” component, and the error will no longer appear inside the JavaScript Console.

The reason this works is because arrow functions don’t create their own context. In other words, the value of this doesn’t get overwritten. The value of this will remain equal to whatever this is equal to outside of the function. It’s for this reason that, when working with React, I generally recommend using arrow functions by default. There are some instances where arrow functions aren’t ideal, but most of the time, they’re what you want.

At this point, open the React DevTools and try changing the value of the state object’s “selectedPlayer” property.

You’ll notice that, each time you change the “selectedPlayer” value, that change is automatically reflected within the interface as the “selected” class is applied to appropriate li element.

To end this section, delete the “data” object from the render method of the “PlayerList” component, as it’s no longer needed.

Passing Methods

Over the next couple of sections, we want to (finally) allow users to select a player in the list by clicking on them.

At the moment, we have the “selectedPlayer” value in the state object of the “PlayerList” component and the “handleClick” method in the “PlayerListItem” component. These are the two pieces of the puzzle that will allow us to achieve our goal. But we need to connect them together.

Specifically, what we want to happen is that, when a user clicks on a player, such as the player with the ID of “6”, the “selectedPlayer” value will change to “6”. When that happens, the “selected” class will be attached to the player with the ID of “6”.

There is, however, a problem:

Before, we talked about how the state object of a component is attached to that specific component. What’s significant about this is that **the only way we can change the value of state is from inside the component that the state is attached **.

In other words, because the “selectedPlayer” value is inside the “PlayerList” component, the “handleClick” method inside the “PlayerListItem” component cannot affect the “selectedPlayer” value.

Or at least, not directly.

What we can do is create a method inside the “PlayerList” component that is capable of changing the “selectedPlayer” value, and then pass that method into the “PlayerListItem” component as a prop. We can then trigger the execution of that method from inside the “PlayerListItem” component’s “handleClick” method.

This might sound a little tricky, but let’s see how it works.

Inside the “PlayerList” component, create a method named “selectPlayer”:

selectPlayer(){
    // code goes here
}

…and place a console.log statement inside this method:

selectPlayer(){
    console.log("This is the selectPlayer() method");
}

Then pass this “selectPlayer” method into the “PlayerListItem” component as a prop named “selectPlayer”:

const listOfPlayers = playerData.map((player) => {
    console.log(this);
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={this.state.selectedPlayer}
            selectPlayer={this.selectPlayer} />
    );
});

Here, we’ve passed the “selectPlayer” method as a prop, just like we might pass a variable or data from the state object as a prop.

Return to the “handleClick” method of the “PlayerListItem” component:

handleClick(){
    console.log(`You clicked on ${this.props.name}, who has a score of ${this.props.score}.`);
}

…and, from inside this method, reference the “selectPlayer” method that we’ve passed into the component as a prop:

handleClick(){
    this.props.selectPlayer();
}

Take note of the parentheses that we’ve added to the end of the prop name. If we don’t include these parentheses, React won’t know that we want to execute the method, so make sure you include them.

Because of this code, each time we click on one of the players in the list, the “handleClick” method inside the “PlayerListItem” component will trigger the execution of the “PlayerList” component’s “selectPlayer” method.

Setting State

In a moment, we’re going to use the “selectPlayer” method to change the value of the “selectedPlayer” property, inside the state object.

Before we can do this though, the “selectPlayer” method needs access to the ID of the player that has just been clicked.

To do this, change the “handleClick” method from this:

handleClick(){
    this.props.selectPlayer();
}

…to this:

handleClick(){
    this.props.selectPlayer(this.props.id);
}

Here, we’re passing the ID of the clicked player from the “PlayerListItem” component, into the “selectPlayer” method.

Next, return to the “selectPlayer” method:

selectPlayer(){
    console.log("This is the selectPlayer() method");
}

…and allow this method to accept the value that is being passed into it:

selectPlayer(id){
    console.log(id);
}

With this code in place, click on any player in the list and notice that their ID is being output to the JavaScript Console.

On the surface, this seems similar to functionality we created earlier, but what’s significant is that the ID is available inside the “selectPlayer” method. Earlier, we could only access this value from inside the “PlayerListItem” component. We’ve basically transported the ID upward, from the “PlayerListItem” component and into the “PlayerList” component.

Because of this change, we can now use the “selectPlayer” method to change the value of the “selectedPlayer” property inside the state object.

To achieve this, you might expect that we do something like this:

selectPlayer(id){
    this.state = {
        selectedPlayer: id
    }
}

But a fundamental rule within React is that we never modify the state object directly. We can define the default state from inside the constructor method, but anytime we want to modify state beyond that point, we need to use the setState function.

Here is what setState looks like:

selectPlayer(id){
    this.setState({
        selectedPlayer: id
    });
}

As we can see, all we have to do is pass through a JavaScript object into the setState function to set that object as the state of the current component.

There is, however, a problem.

Within this code in place, if we click on any of the players in the list, this error will appear in the JavaScript Console:

Uncaught TypeError: this.setState is not a function

The reason we’re encountering this error is because we’ve created this custom “selectPlayer” method, so by default, this inside of this method is not equal to the “PlayerList” component, which it needs to be.

To fix this error, we can attach the bind method to the “selectPlayer” method as we’re passing it into the “PlayerListItem” component as a prop:

const listOfPlayers = playerData.map((player) => {
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={this.state.selectedPlayer}
            selectPlayer={this.selectPlayer.bind(this)} />
    );
});

Based on this change, users will now be able to click on any of the players within the list to select them.

It’s taken us quite a long time to get here, but we’ve covered a lot of ground already, and there’s plenty more of React’s features for us to explore.

ES7 Binding

Earlier, I mentioned that there’s a few different ways to work around the issue of binding in a React project.

At the moment, for instance, we’re using the bind method whenever we reference a custom method that references this from inside of it:

const listOfPlayers = playerData.map((player) => {
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={this.state.selectedPlayer}
            selectPlayer={this.selectPlayer.bind(this)} />
    );
});

The good news is, there’s a feature in ES7 – the next version of JavaScript – that allows us to simplify binding to the point that we really don’t have to think about it at all.

To see this feature in action, return to the “selectPlayer” method:

selectPlayer(id){
    this.setState({
        selectedPlayer: id
    });
}

…and change this method to use the arrow syntax:

selectPlayer = (id) => {
    this.setState({
        selectedPlayer: id
    });
}

Then remove the bind method from inside the map method:

const listOfPlayers = playerData.map((player) => {
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={this.state.selectedPlayer}
            selectPlayer={this.selectPlayer} />
    );
});

You’ll notice that, although we’ve removed the bind method, the component continues to work as expected. You’re able to click and select players without issue.

What’s going on?

Before, we talked about how arrow functions don’t create their own context – meaning, if we create an arrow function, this inside the function will be equal to whatever this is equal to outside of that function.

The same applies to methods within a class.

If we use the arrow syntax, any reference to this from inside a method remain equal to the component that the method is contained within.

As a result, we don’t need to use the bind method.

The problem is, this feature is experimental. We do have access to this feature when working with React – it’s something that create-react-app makes available to us – but the feature might not always exist.

If you prefer this ES7 syntax though, I think it’s perfectly reasonable to make the most of it. Just ensure that you remain aware of any changes to ES7 to prevent your project from breaking all a sudden.

It is, however, worth noting that this isn’t the only alternative way to take care of binding. It is the most elegant option, but there’s other styles that exist that we’ll talk about later on.

Interface

In this chapter, we’re going to start working on the functionality that allows users to change the score of the selected player by clicking the “Give 5 Points” and “Take 5 Points” buttons. This seemingly simple feature will allow us to cover a handful of new concepts, while also allowing us to explore a few things we’ve already learned in new contexts.

Player State

At the moment, there’s a “playerData” array inside the “PlayerList” component that holds all of the data related to the players in the list:

const playerData = [
    { id: 1, name: "David", score: 0 },
    { id: 2, name: "Bob", score: 0 },
    { id: 3, name: "Mary", score: 0 },
    { id: 4, name: "Tim", score: 0 },
    { id: 5, name: "Warren", score: 0 },
    { id: 6, name: "Bill", score: 0 }
];

When a user clicks one of the “Give 5 Points” or “Take 5 Points” buttons, we want the “score” value of the selected player to be changed, but if this data is changed, those changes won’t be reflected in the interface.

Why?

Because, if the changes are to be reflected in the interface, we need to store this data inside the component’s state object.

With this in mind, add a property named “players” to the state object, inside the “PlayerList” component’s constructor method:

constructor(){
    super();
    this.state = {
        selectedPlayer: 3,
        players:
    }
}

…and set the “playerData” array as the value of this property:

constructor(){
    super();
    this.state = {
        selectedPlayer: 3,
        players: [
            { id: 1, name: "David", score: 0 },
            { id: 2, name: "Bob", score: 0 },
            { id: 3, name: "Mary", score: 0 },
            { id: 4, name: "Tim", score: 0 },
            { id: 5, name: "Warren", score: 0 },
            { id: 6, name: "Bill", score: 0 }
        ]
    }
}

Next, make sure that the map method is using “this.state.players” as the data source, instead of the “playerData” array:

const listOfPlayers = this.state.players.map((player) => {
    return (
        <PlayerListItem
            key={player.id}
            id={player.id}
            name={player.name}
            score={player.score}
            selectedPlayer={this.state.selectedPlayer}
            selectPlayer={this.selectPlayer} />
    );
});

Then delete the “playerData” array.

The interface won’t look any different but, based on this change, we’ll be able to open the React DevTools, change the player’s names and scores, and see those changes instantly reflected within the interface.

As a result, we can start building the functionality that allows players to change the scores of players.

Player Controls

Earlier, we made the “PlayerControls” component. This is the component that contains the “Give 5 Points” and “Take 5 Points” buttons.

Here’s what it looks like:

function PlayerControls(){
    return (
        <div>
            <button>Give 5 Points</button>
            <button>Take 5 Points</button>
        </div>
    );
}

In this section, we’re going to attach functionality to these buttons and allow them to affect the “players” state that we setup a moment ago.

To begin, create a function named “handleGiveClick” inside this component:

function PlayerControls(){
    function handleGiveClick(){
        console.log("Give points to player");
    }
    return (
        <div>
            <button>Give 5 Points</button>
            <button>Take 5 Points</button>
        </div>
    );
}

…and pass this function into the onClick handler for the relevant button:

function PlayerControls(){
    function handleGiveClick(){
        console.log("Give points to player");
    }
    return (
        <div>
            <button onClick={handleGiveClick}>Give 5 Points</button>
            <button>Take 5 Points</button>
        </div>
    );
}

Then repeat this process with a function named “handleTakeClick”:

function PlayerControls(){
    function handleGiveClick(){
        console.log("Give points to player");
    }
    function handleTakeClick(){
        console.log("Take points from a player");
    }
    return (
        <div>
            <button onClick={handleGiveClick}>Give 5 Points</button>
            <button onClick={handleTakeClick}>Take 5 Points</button>
        </div>
    );
}

With this code in place, both of these buttons should now output the relevant message to the JavaScript Console.

But what we need to do next is allow these functions we’ve created to modify the state object of the “PlayerList” component.

There is, however, a problem:

We can only modify the state object from inside the component where that state object exists. This means it’s not possible for the “hanndleGiveClick” or “handleTakeClick” functions to modify the state object of the “PlayerList” component.

Earlier, we encountered a similar problem. We had the “selectedPlayer” value inside the state object of the “PlayerList” component, but we needed to modify this value when the “handleClick” function of the “PlayerListItem” component was triggered. To solve this problem, we created a “selectPlayer” method inside the “PlayerList” component – meaning, the method existed in the same component as the state object we wanted to modify – and we passed that method into the “PlayerListItem” component. This allowed the “handleClick” function to trigger the “selectPlayer” method and modify the state object of the “PlayerList” component, even though the “handleClick” function didn’t have direct access to that state object.

In this case though, we can’t use this same solution.

Why?

Because the “PlayerControls” component is not a child of the “PlayerList” component. This means we can’t pass a method from the “PlayerList” component into the “PlayerControls” component. The “PlayerControls” and “PlayerList” components are siblings, which makes things a little more difficult.

Fortunately, this isn’t an uncommon problem.

In fact, this is actually the fourth step of the “Thinking in React” process, which instructs us to, “Identify Where Your State Should Live”.

The purpose of this step is to figure out where should put our state objects. At the moment, we have a single state object inside the “PlayerList” component, but there’s no reason the state object has to be in this component. We could move the state object up, into a component that is a parent to the “PlayerControls” and “PlayerList” components, and then pass that data down to where it’s needed.

Why would we do such a thing?

Because it would allow all of the components to access the data they need.

To see this in action, we’re going to move the state object from the “PlayerList” component and into the “App” component. That way, passing data from the state object and into the rest of the components will be much more straightforward.

At the moment, this is what the “App” component looks like:

function App(){
    return (
        <div>
            <AddPlayerForm />
            <PlayerList />
            <PlayerControls />
        </div>
    );
}

But, as we’ve discussed, for a component to have a state object, it needs to be a class component.

Here’s what the “App” component looks like as a class component:

class App extends React.Component{
    render(){
        return (
            <div>
                <AddPlayerForm />
                <PlayerList />
                <PlayerControls />
            </div>
        );
    }
}

Next, add a constructor method to this component, making sure to include the super keyword at the top of the method:

class App extends React.Component{
    constructor(){
        super();
    }
    render(){
        return (
            <div>
                <AddPlayerForm />
                <PlayerList />
                <PlayerControls />
            </div>
        );
    }
}

Then copy the state object from the “PlayerList” component and paste it into the “App” component:

class App extends React.Component{
    constructor(){
        super();
        this.state = {
            selectedPlayer: 3,
            players: [
                { id: 1, name: "David", score: 0 },
                { id: 2, name: "Bob", score: 0 },
                { id: 3, name: "Mary", score: 0 },
                { id: 4, name: "Tim", score: 0 },
                { id: 5, name: "Warren", score: 0 },
                { id: 6, name: "Bill", score: 0 }
            ]
        }
    }
    render(){
        return (
            <div>
                <AddPlayerForm />
                <PlayerList />
                <PlayerControls />
            </div>
        );
    }
}

Because of this change, the “PlayerList” component doesn’t need its own state object, which means it doesn’t need to be a class component. You can choose to leave it as a class component if you like, but it’s a best practice to use functional components wherever possible.

This is what the “PlayerList” component looks like as a functional component:

function PlayerList(props){
    const listOfPlayers = this.state.players.map((player) => {
        return (
            <PlayerListItem
                key={player.id}
                id={player.id}
                name={player.name}
                score={player.score}
                selectedPlayer={this.state.selectedPlayer}
                selectPlayer={this.selectPlayer} />
        );
    });
    return (
        <ul>
            {listOfPlayers}
            <li>Selected Player: {this.state.selectedPlayer}</li>
        </ul>
    );
}

Here, there’s a few things worth noticing:

  1. I’ve removed the “selectPlayer” method from this component. That’s because the “selectPlayer” method will need to be moved to the “App” component, so we’ll recreate it there in a moment.

  2. The component refers to the state object, which no longer exists inside this component. As a result, the interface will break and errors will appear in the JavaScript Console. We’ll fix these errors in a moment.

  3. I’ve added the “props” parameter between the parentheses of the function declaration. This will allow us to access props inside this component.

Return to the “App” component and pass the data from the state object into the “PlayerList” component:

render(){
    return (
        <div>
            <AddPlayerForm />
            <PlayerList
                players={this.state.players}
                selectedPlayer={this.state.selectedPlayer} />
            <PlayerControls />
        </div>
    );
}

Then return to the “PlayerList” component and, in three places, replace “this.state” with “props”:

function PlayerList(props){
    const listOfPlayers = props.players.map((player) => {
        return (
            <PlayerListItem
                key={player.id}
                id={player.id}
                name={player.name}
                score={player.score}
                selectedPlayer={props.selectedPlayer}
                selectPlayer={this.selectPlayer} />
        );
    });
    return (
        <ul>
            {listOfPlayers}
            <li>Selected Player: {props.selectedPlayer}</li>
        </ul>
    );
}

Based on these changes, the list of players will once again appear within the interface, but we still won’t be able to select them. This is because we need to recreate the “selectPlayer” method.

To do this, recreate the “selectPlayer” method inside the “App” component:

selectPlayer = (id) => {
    this.setState({
        selectedPlayer: id
    });
}

…and pass this method into the “PlayerList” component:

render(){
    return (
        <div>
            <AddPlayerForm />
            <PlayerList
                players={this.state.players}
                selectedPlayer={this.state.selectedPlayer}
                selectPlayer={this.selectPlayer} />
            <PlayerControls />
        </div>
    );
}

It’s worth noting that, if we didn’t use the ES7 syntax for the “selectPlayer” method:

selectPlayer(id){
    this.setState({
        selectedPlayer: id
    });
}

Then we’d need to use the bind method when passing the method into the “PlayerList” component:

render(){
    return (
        <div>
            <AddPlayerForm />
            <PlayerList
                players={this.state.players}
                selectedPlayer={this.state.selectedPlayer}
                selectPlayer={this.selectPlayer.bind(this)} />
            <PlayerControls />
        </div>
    );
}

But since we’re using the ES7 syntax, this isn’t necessary.

To end this section, all we have to do is return to the “PlayerList” component and change “this.selectPlayer” to “props.selectPlayer”:

function PlayerList(props){
    const listOfPlayers = props.players.map((player) => {
        return (
            <PlayerListItem
                key={player.id}
                id={player.id}
                name={player.name}
                score={player.score}
                selectedPlayer={props.selectedPlayer}
                selectPlayer={props.selectPlayer} />
        );
    });
    return (
        <ul>
            {listOfPlayers}
            <li>Selected Player: {props.selectedPlayer}</li>
        </ul>
    );
}

This ensures that the “selectPlayer” method is passed from the “App” component, down into the “PlayerList” component, and then down into the “PlayerListItem” component.

Changing Scores

In this section, we’re going to create a method that will grab the currently selected player and update the “score” value of the player by modifying the state object that’s inside the “App” component.

To begin, add a method named “changeScore” to the “App” component:

changeScore = () => {
    console.log("You tried changing the score");
}

The reason we’re putting this method in the “App” component is because it’s the method that will take care of modifying the state object, so it needs to be inside the same component as that state object.

Next, pass this method into the “PlayerControls” component:

render(){
    return (
        <div>
            <AddPlayerForm />
            <PlayerList
                players={this.state.players}
                selectedPlayer={this.state.selectedPlayer}
                selectPlayer={this.selectPlayer.bind(this)} />
            <PlayerControls
                changeScore={this.changeScore} />
        </div>
    );
}

Then, inside the “PlayerControls” component, add this “changeScore” method inside the “handleGiveClick” and “handleTakeClick” functions:

function PlayerControls(props){
    function handleGiveClick(){
        props.changeScore();
    }
    function handleTakeClick(){
        props.changeScore();
    }
    return (
        <div>
            <button onClick={handleGiveClick}>Give 5 Points</button>
            <button onClick={handleTakeClick}>Take 5 Points</button>
        </div>
    );
}

Here, there’s two things worth noticing:

  1. I’ve added the “props” parameter to the component’s function declaration, to ensure that we can access props within this component.
  2. I’ve added parentheses after “props.changeScore” to ensure that the method is executed. Without the parentheses, the method would never be executed.

With this code in place, clicking on either of these buttons will output the “You tried changing the score” message to the JavaScript Console.

For the “handleGiveClick” and “handleTakeClick” functions to know which player’s score needs to be updated, they need to be able to access the ID of the currently selected player.

We can do this by passing “this.state.selectedPlayer” from the “App” component and into the “PlayerControls” component:

render(){
    return (
        <div>
            <AddPlayerForm />
            <PlayerList
                players={this.state.players}
                selectedPlayer={this.state.selectedPlayer}
                selectPlayer={this.selectPlayer.bind(this)} />
            <PlayerControls
                selectedPlayer={this.state.selectedPlayer}
                changeScore={this.changeScore}/>
        </div>
    );
}

As a result, we’ll have access to the “selectedPlayer” prop from inside the “PlayerControls” component, and we can then pass this value back into the “changeScore” methods:

function PlayerControls(props){
    function handleGiveClick(){
        props.changeScore(props.selectedPlayer);
    }
    function handleTakeClick(){
        props.changeScore(props.selectedPlayer);
    }
    return (
        <div>
            <button onClick={handleGiveClick}>Give 5 Points</button>
            <button onClick={handleTakeClick}>Take 5 Points</button>
        </div>
    );
}

Because of this code, the “changeScore” method inside the “App” component will know which player is currently selected when the “Give 5 Points” or the “Take 5 Points” buttons have been clicked.

But both of these methods need to know how much the player’s score should be changed by, so let’s pass through values of “5” and “-5”:

function PlayerControls(props){
    function handleGiveClick(){
        props.changeScore(props.selectedPlayer, 5);
    }
    function handleTakeClick(){
        props.changeScore(props.selectedPlayer, -5);
    }
    return (
        <div>
            <button onClick={handleGiveClick}>Give 5 Points</button>
            <button onClick={handleTakeClick}>Take 5 Points</button>
        </div>
    );
}

Later, we’re going to refactor these methods and make it (even more) trivial to change these specific values, but this approach is good enough for now.

Next, return to the “changeScore” method and allow the method to accept both of the values that are being passed into it:

changeScore = (selectedPlayer, amount) => {
    console.log("You tried changing the score");
}

Here, I’ve defined these “selectedPlayer” and “amount” parameters.

To see the effect of this, modify the console.log statement:

changeScore = (selectedPlayer, amount) => {
    console.log(`You tried changing the score of ${selectedPlayer} by a value of ${amount}.`);
}

Then select any player in the list and click either the “Give 5 Points” or “Take 5 Points” button. You’ll see the above message appear in the JavaScript Console with the select player’s details filled in.

But now we have access to the relevant data inside the “changeScore” method, we need to actually use that data to update the selected player.

There’s a few different ways of approaching this, and more effort will be required when working with complex data structures, but in this case – and in many cases – the simplest solution is likely the best bet.

Inside the “changeScore” method, create a map method that loops through the “players” property of the component’s state object:

changeScore = (selectedPlayer, amount) => {
    this.state.players.map((player) => ){
        // code goes here
    }
}

Then store this map method inside a variable named “updatedPlayers”:

changeScore = (selectedPlayer, amount) => {
    const updatedPlayers = this.state.players.map((player) => ){
        // code goes here
    }
}

You’ll notice that we’re passing an arrow function into the map method. In this case, this isn’t necessary, as we won’t be referencing this from inside this function, but I still consider using an arrow function a best practice.

Next, create a conditional inside the map method that checks whether the ID of the current player matches the ID in the “selectedPlayer” variable:

changeScore = (selectedPlayer, amount) => {
    const updatedPlayers = this.state.players.map((player) => ){
        if(player.id === selectedPlayer){
            // code goes here
        }
    }
}

This is how we ensure that we only affect the data of the selected player.

Inside this conditional, take the “score” property of the current player and add the value stored in the “amount” variable to this property:

changeScore = (selectedPlayer, amount) => {
    const updatedPlayers = this.state.players.map((player) => ){
        if(player.id === selectedPlayer){
            player.score = player.score + amount;
        }
    }
}

You can, of course, use the short-hand version of this expression:

player.score += amount;

Then, after the conditional, return the “player” object:

changeScore = (selectedPlayer, amount) => {
    const updatedPlayers = this.state.players.map((player) => ){
        if(player.id === selectedPlayer){
            player.score += amount;
        }
        return player;
    }
}

Because of this code, the following will happen every time the “changeScore” method is executed:

  1. All of the players from “this.state.players” will be looped through by the map method.
  2. The object of the currently selected player will have its “score” property updated. If a value of “5” has been passed through, the “score” property will increase by 5. If a value of “-5” has been passed through, the “score” property will decrease by 5.
  3. The map method will create a new array that contains the updated player’s data.

The important thing to understand is that the “updatedPlayers” array contains all of the players from “this.state.players”.

To see this in action, use a console.log statement:

changeScore = (selectedPlayer, amount) => {
    const updatedPlayers = this.state.players.map((player) => ){
        if(player.id === selectedPlayer){
            player.score += amount;
        }
        return player;
    }
    console.log(updatedPlayers);
}

If you select any of the players and click the “Give 5 Points” or “Take 5 Points” buttons, the updated array will appear in the JavaScript Console.

To have the updated data appear in the interface though, we need to use the setState function from earlier:

changeScore = (selectedPlayer, amount) => {
    const updatedPlayers = this.state.players.map((player) => ){
        if(player.id === selectedPlayer){
            player.score += amount;
        }
        return player;
    }
    this.setState({
        players: updatedPlayers
    });
}

Based on this change, users can now select players and modify their scores, with the changes to those scores being updated in real time.

Sorting Players

At the moment, the players in our list are sorted by their position in the “players” array. The player named “David”, for instance, will always appear first in the list of players. But it would make a lot more sense if players were sorted by their scores and if the position of the players changed in real-time, as their scores were being modified by the user.

It’s possible to achieve this functionality with pure JavaScript, but to make our life easier, we’re going to use the Lodash library.

What is “Lodash”?

Lodash is a utility library for JavaScript. This means it’s a library that allows us to quickly solve common problems during JavaScript development. For example, Lodash has a sortBy function for sorting objects in an array.

Technically, we don’t need Lodash.

Anything that can be done with Lodash can be done without Lodash – especially with the introduction of ES6 and ES7 – but Lodash remains particularly simple and intuitive, making it a popular library among many React developers.

To add Lodash to our project, we can install it via the NPM repository. All we have to do is open the command line and run the following command:

npm install lodash --save

This will add the “lodash” package to the “package.json” file, allowing us to use the library throughout our project.

Before we can start using the library through, we need to import it into our project. This means using the import keyword from earlier.

At the top of the “index.js” file, add the following statement:

import _ from 'lodash'

But this might look a little weird.

“What’s with the underscore?”

In JavaScript, an underscore is a valid name for a variable, and when using Lodash, it’s convention to refer to the library with such an underscore.

We could choose to refer to it as “lodash”:

import lodash from 'lodash'

But the underscore requires fewer keystrokes and makes it particularly simple to identify what parts of our code relies upon the Lodash library.

Next, return to the “PlayerList” component:

function PlayerList(props){
    const listOfPlayers = props.players.map((player) => {
        return (
            <PlayerListItem
                key={player.id}
                id={player.id}
                name={player.name}
                score={player.score}
                selectedPlayer={props.selectedPlayer}
                selectPlayer={props.selectPlayer} />
        );
    });
    return (
        <ul>
            {listOfPlayers}
            <li>Selected Player: {props.selectedPlayer}</li>
        </ul>
    );
}

…and, above the “listofPlayers” variable, create a variable named “sortedPlayers”:

const sortedPlayers;

Then make this variable equal to Lodash’s sortBy function:

const sortedPlayers = _.sortBy();

This function accepts two arguments: the array (or object) that you want to sort, and an array of properties that you want to sort by.

In this case, we want to sort the data inside “props.players”:

const sortedPlayers = _.sortBy(props.players);

…and we want to sort by the “score” property:

const sortedPlayers = _.sortBy(props.players, ['score']);

With this code in place, change the map method so its data is coming from the “sortedPlayers” variable, rather than from “props.players”:

function PlayerList(props){
    const sortedPlayers = _.sortBy(props.players, ['score']);
    const listOfPlayers = sortedPlayers.map((player) => {
        return (
            <PlayerListItem
                key={player.id}
                id={player.id}
                name={player.name}
                score={player.score}
                selectedPlayer={props.selectedPlayer}
                selectPlayer={props.selectPlayer} />
        );
    });
    return (
        <ul>
            {listOfPlayers}
            <li>Selected Player: {props.selectedPlayer}</li>
        </ul>
    );
}

There’s a problem though.

The sortBy function only sorts in ascending order. This means, if we change the scores of some of the players, we’ll see that the players are sorted from the lowest score to the highest, which isn’t what we want.

To resolve this problem, we can instead use the orderBy function:

const sortedPlayers = _.orderBy(props.players, ['score']);

This function allows us to pass through a third argument, where we can define how the data should be sorted. In this case, for instance, we want to sort the data in descending order:

const sortedPlayers = _.orderBy(props.players, ['score'], ['desc']);

This ensures that players are sorted from highest score to lowest score.

There is, however, still a slight problem.

You might notice that, even if “Bob” and “Bill” have the same scores, “Bob” is ranked above “Bill”. This is because “Bob” comes first in the “players” array. But ideally, if “Bob” and “Bill” have the same score, “Bill” would be ranked first because the name “Bill” comes before “Bob” alphabetically.

If this seems pedantic, fair enough, but it’s not a difficult change to make.

Inside the orderBy function, add the “name” property to the second argument:

const sortedPlayers = _.orderBy(props.players, ['score', 'name'], ['desc']);

…and a value of “asc” to the third argument:

const sortedPlayers = _.orderBy(props.players, ['score', 'name'], ['desc', 'asc']);

Based on these changes, players will be sorted by their scores in descending order and also sorted by their names in ascending order. As a result, when two players have the same score, those players will appear in alphabetical order.

Forms

In this chapter, we’re going to allow users to both add and remove players from the list of players. This will be the last, essential feature that we create, but there’s still plenty of interesting things left to discuss.

Creating Forms

At the moment, this is what the “AddPlayerForm” looks like:

function AddPlayerForm(){
    return (
        <form>
            <input type="text" />
            <button type="submit">Add Player</button>
        </form>
    );
}

But, as we’ll see, the typical way of working with forms in React requires that this component has a state object. As such, we’ll need to convert this component into a class component.

As a class component, this is what the “AddPlayerForm” component looks like:

class AddPlayerForm extends React.Component{
    render(){
        return (
            <form>
                <input type="text" />
                <button type="submit">Add Player</button>
            </form>
        );
    }
}

Inside this component, create a method named “handleSubmit”:

class AddPlayerForm extends React.Component{
    handleSubmit = () => {
        console.log("You submitted the form");
    }
    render(){
        return (
            <form>
                <input type="text" />
                <button type="submit">Add Player</button>
            </form>
        );
    }
}

…and attach an onSubmit handler to the form element:

class AddPlayerForm extends React.Component{
    handleSubmit = () => {
        console.log("You submitted the form");
    }
    render(){
        return (
            <form onSubmit={this.handleSubmit}>
                <input type="text" />
                <button type="submit">Add Player</button>
            </form>
        );
    }
}

Because of this code, submitting the form will trigger the execution of the “handleSubmit” method.

If you actually try submitting the form though, the “You submitted the form” message will only flicker briefly in the JavaScript Console and the page will refresh.

This is because, by default, forms are expected to send data somewhere, but since we haven’t told the form where we want to send that data, the browser simply refreshes the page.

To solve this problem, we need to prevent the browser from applying default behavior to the form. That way, we can control over how the form behaves.

Between the parentheses of the “handleSubmit” method, define a parameter named “event”:

handleSubmit = (event) => {
    console.log("You submitted the form");
}

Then, inside the method, attach a preventDefault function to the event:

handleSubmit = (event) => {
    event.preventDefault();
    console.log("You submitted the form");
}

This is a standard JavaScript function that ensures we have complete control over the behavior of the form. It works by telling the form to do nothing by default, allowing us to define our own, customized behavior.

Next, we want to extract the value of the input field. If we were using a library like jQuery, this is how we’d achieve that:

$('input').val();

But when working with React, we have to do things a little differently.

Why?

Because one of the core features of React is that it takes care of inserting and updating parts of the interface for us.

For example, when we change the score of a player, we don’t have to tell React to explicitly update that value within the interface. We only have to define what the data is and where it should be displayed. React does the rest.

If you’d like to learn more about this process, then you’ll want to look into the “Virtual DOM” feature of React. In practice though, here’s the important thing to understand:

When using React, it’s a bad practice to grab something directly out of the DOM, as we might do with jQuery.

Instead, we have to take a slightly odd – but ultimately, quite powerful – approach.

To begin, create a constructor method inside the “AddPlayerForm” component:

constructor(){
    super();
}

…and create a state object inside this method:

constructor(){
    super();
    this.state = {
        // state goes here
    }
}

Inside this state object, create a property named “playerName” that, by default, is set to an empty string:

constructor(){
    super();
    this.state = {
        playerName: ""
    }
}

We’ll see the point of this state object in a moment.

Next, create a method named “handleChange” inside the “AddPlayerForm” component:

handleChange = () => {
    console.log("Value has changed");
}

…and add an onChange event handler to the input field, using it to trigger the execution of the “handleChange” method:

render(){
    return (
        <form onSubmit={this.handleSubmit}>
            <input type="text" onChange={this.handleChange} />
            <button type="submit">Add Player</button>
        </form>
    );
}

Because of this code, the “Value has changed” message will appear inside the JavaScript Console whenever the value of the input field changes. To see this in action, try typing anything into this field.

Inside the “handleChange” method, we want to be able to access the current value of the input field. This is possible by creating an “event” parameter between the parentheses of the “handleChange” method:

handleChange = (event) => {
    console.log("Value has changed");
}

…and referring to event.target from inside the method:

handleChange = (event) => {
    console.log(event.target);
}

event.target refers to the element that the event is attached to. Therefore, with this code in place, the input field will appear in the JavaScript Console.

But we don’t want to grab the field itself. We want to grab the value of that field. To do this, we can grab the “value” property of event.target:

handleChange = (event) => {
    console.log(event.target.value);
}

At this point, whatever the user types into the input field will appear in the JavaScript Console.

But what’s critical to understand is that, when we’re working with React, any part of the interface that has the potential to change – such as the value of an input field – should be stored in state. This allows us to have complete control over what the user sees, without any weird side-effects.

In practice, this means we want to save the value of the input field to the component’s state object.

To do this, add the setState method inside the “handleChange” method:

handleChange = (event) => {
    this.setState({
        playerName: event.target.value
    });
}

Here, we’re setting the value of “playerName” property inside the state object to the value that is currently inside the input field.

To see the effect of this code, open the React DevTools, select the “AddPlayerForm” component, and try modifying the value of the input field. You’ll see the component’s state object updating with each change.

This confirms that we always have access to the value inside the input field despite not reaching into the DOM and grabbing anything.

To end this section, add a value attribute to the input field and set it equal to “this.state.playerName”:

render(){
    return (
        <form onSubmit={this.handleSubmit}>
            <input type="text" value={this.state.playerName} onChange={this.handleChange} />
            <button type="submit">Add Player</button>
        </form>
    );
}

This code might seem a little redundant, since our input field seemed to be working fine just the way it was, but what’s going on here is that we’re ensuring the value inside the input field and the “playerName” value inside the state object are always perfectly synchronized. This is important as we want to ensure that there is absolutely zero possibility that the interface doesn’t reflect what’s inside the component’s state object.

If you’re starting to think that creating forms in React seems like an overly cumbersome process, that’s fair enough. It definitely is one of the less elegant parts of working with React. But there is also a lot of power in this approach, which we’ll talk about shortly.

Adding Players

Because we’re storing the “playerName” value inside the state object of the “AddPlayerForm” component, we can access this value inside the “handleSubmit” method:

handleSubmit = (event) => {
    event.preventDefault();
    console.log(`You tried adding ${this.state.playerName} to the list.`);
}

To see in this code in action, enter a value into the input field and try submitting the form.

But, of course, we need to get this value from the “AddPlayerForm” component and into the “players” array that’s inside the “App” component’s state object. Only then will the player appear within the list of players.

To achieve this, return to the “App” component and create a method named “addPlayer”:

addPlayer = () => {
    // code goes here
}

Then allow this method to accept an argument named “name”:

addPlayer = (name) => {
    console.log(`Adding ${name} to the list...`);
}

…and pass this method into the “AddPlayerForm” component as a prop named “addPlayer”:

render(){
    return (
        <div>
            <AddPlayerForm addPlayer={this.addPlayer} />
            <PlayerList
                players={this.state.players}
                selectedPlayer={this.state.selectedPlayer}
                selectPlayer={this.selectPlayer.bind(this)} />
            <PlayerControls
                selectedPlayer={this.state.selectedPlayer}
                changeScore={this.changeScore}/>
        </div>
    );
}

Back inside the “AddPlayerForm” component, modify the “handleSubmit” method so it executes the “addPlayer” method, making sure to pass through the “playerName” value as the first and only argument:

handleSubmit = (event) => {
    event.preventDefault();
    this.props.addPlayer(this.state.playerName);
}

This code is similar to what we had a moment ago – it allows users to type a value into the input field, submit the form, and see that value appear in the JavaScript Console – but the significant thing is that the “addPlayer” method now has access to the value of the “AddPlayerForm” component’s input field.

With this code in place, return to the “addPlayer” method and create an object named “newPlayer”:

addPlayer = (name) => {
    const newPlayer = {}
}

This object will contain all of the data for a player to display properly within the interface, including an ID, name, and score.

For the ID, we can’t hard-code the value, since the ID needs to be unique for each of the players. In a real-world application, the ID would likely be coming from an external data source – such as a JSON file – and that data source would likely have its own system in place for ensuring that the IDs are unique. Since we don’t have a back-end though, we’ll do something simple:

addPlayer = (name) => {
    const id = new Date().getUTCMilliseconds();
    const newPlayer = {
        id: id
    }
}

Here, we’re using this getUTCMilliseconds method to generate an ID for each of our players, based on the current date and time. To be clear, this method does not guarantee that the ID will be unique, so it’s not a fool-proof solution, but for this example, the IDs will be “unique enough”.

Next, define a “name” and a “score” property inside the “newPlayer” object, and output this object to the JavaScript Console:

addPlayer = (name) => {
    const id = new Date().getUTCMilliseconds();
    const newPlayer = {
        id: id,
        name: name,
        score: 0
    }
    console.log(newPlayer);
}

Then confirm this code works as expected by entering a value into the input field and submitting the form.

It’s worth noting that, when creating the “newPlayer” object, because the “id” key has a value of “id”, and the “name” key has a value of “name”, we can make our code a little bit less redundant:

addPlayer = (name) => {
    const id = new Date().getUTCMilliseconds();
    const newPlayer = {
        id,
        name,
        score: 0
    }
    console.log(newPlayer);
}

This is a nifty feature of the ES6 syntax.

At this point, we want to add the “newPlayer” object to the “players” array that’s inside the component’s state object.

What we can’t do is the following:

this.state.players.push(newPlayer);

The reason we can’t use this statement is because it modifies the state object directly, which isn’t how things are done in React. Instead, what we need to do is create a new array to overwrite the old array.

To do this, we can use the concat method:

addPlayer = (name) => {
    const id = new Date().getUTCMilliseconds();
    const newPlayer = {
        id,
        name,
        score: 0
    }
    const updatedPlayers = this.state.players.concat(newPlayer);
}

The concat method is similar to the push method, except for the fact that it creates an entirely new array.

With this code in place, all we have to do is pass the “updatedPlayers” array into the setState method:

addPlayer = (name) => {
    const id = new Date().getUTCMilliseconds();
    const newPlayer = {
        id,
        name,
        score: 0
    }
    const updatedPlayers = this.state.players.concat(newPlayer);
    this.setState({ players: updatedPlayers });
}

As a result, users will now be able to type a value into the input field, submit the form, and see that player appear within the player’s list.

There is, however, a lingering problem:

Whenever we submit the form, the value inside the input field is left behind, which doesn’t make for the most elegant user experience.

To fix this problem, return to the “AddPlayerForm” component and change the “handleSubmit” method to the following:

handleSubmit = (event) => {
    event.preventDefault();
    this.props.addPlayer(this.state.playerName);
    this.setState({ playerName: "" });
}

Here, the “playerName” value is being reset to an empty string after the form is submitted. This ensures that, after a player is added to the list, the input field is reset to an empty value.

Validation

So far, we’ve written a lot of code to create a form with React, but the form itself isn’t that remarkable. It seems like a lot of work for not much reward. But where forms in React can start to shine is when it comes to validation.

Why?

Because the value inside the input field is stored inside the component’s state object, which means we can validate the field in real-time.

To see a basic example of this, we’re going to disable the “Add Player” button if the input field is empty. This will provide a useful bit of visual feedback to the user to remind them to enter a value into the field.

Inside the “AddPlayerForm” component’s render method, create a variable named “isDisabled” that is equal to true:

render(){
    let isDisabled = true;
    return (
        <form onSubmit={this.handleSubmit}>
            <input type="text" value={this.state.playerName} onChange={this.handleChange} />
            <button type="submit">Add Player</button>
        </form>
    );
}

I’ve used the let keyword to define this variable, as we’re going to change the value of this variable, which means we can’t use the const keyword:

Next, create a conditional that checks whether “this.state.playerName” is not an empty string:

let isDisabled = true;
if(this.state.playerName != ""){
    // code goes here
}

If “this.state.playerName” is a not empty string, set the “isDisabled” variable to a value of false:

let isDisabled = true;
if(this.state.playerName != ""){
    isDisabled = false;
}

With this code in place, add a disabled attribute to the button element that is equal to this “isDiabled” variable that we’ve defined:

render(){
    let isDisabled = true;
    if(this.state.playerName != ""){
        isDisabled = false;
    }
    return (
        <form onSubmit={this.handleSubmit}>
            <input type="text" value={this.state.playerName} onChange={this.handleChange} />
            <button type="submit" disabled={isDisabled}>Add Player</button>
        </form>
    );
}

Then switch back to the browser and notice that, by default, the “Add Player” button is disabled. But as soon as you type anything into the input field, the “Add Player” button becomes active. If, after that point, you delete the contents of the input field, the button will once again be disabled. All of this happens in real-time, with very little code written.

It’s worth noting, however, that we can simplify the conditional into a ternary operator:

const isDisabled = (this.state.playerName === "") ? true : false;

This code performs the same function with fewer characters.

Removing Players

If it’s possible for users to add players to the leaderboard, it makes sense that users should be able to remove players from the leaderboard.

To achieve this, create a new component named “RemovePlayerButton”:

function RemovePlayerButton(){
    return (
        <button>Remove Player</button>
    );
}

Then include this component inside the “PlayerControls” component:

function PlayerControls(props){
    function handleGiveClick(){
        props.changeScore(props.selectedPlayer, 5);
    }
    function handleTakeClick(){
        props.changeScore(props.selectedPlayer, -5);
    }
    return (
        <div>
            <button onClick={handleGiveClick}>Give 5 Points</button>
            <button onClick={handleTakeClick}>Take 5 Points</button>
            <RemovePlayerButton />
        </div>
    );
}

The button should now appear within the interface.

Inside the “RemovePlayerButton” component, create a “handleClick” function:

function RemovePlayerButton(){
    function handleClick(){
        console.log("You tried removing a player...");
    }
    return (
        <button>Remove Player</button>
    );
}

…and pass this function into the onClick event handler for the button element:

function RemovePlayerButton(){
    function handleClick(){
        console.log("You tried removing a player...");
    }
    return (
        <button onClick={handleClick}>Remove Player</button>
    );
}

For this button to do anything, it will need access to ID of the currently selected player.

To allow for this, return to the “PlayerControls” component and pass the “selectedPlayer” prop into the “RemovePlayerButton” component:

return (
    <div>
        <button onClick={handleGiveClick}>Give 5 Points</button>
        <button onClick={handleTakeClick}>Take 5 Points</button>
        <RemovePlayerButton selectedPlayer={props.selectedPlayer} />
    </div>
);

Then allow the “RemovePlayerButton” component to accept this prop and output it to the JavaScript Console:

function RemovePlayerButton(props){
    function handleClick(){
        console.log(`You tried removing player ${props.selectedPlayer} from the list.`);
    }
    return (
        <button onClick={handleClick}>Remove Player</button>
    );
}

If you select a player and click the “Remove Player” button, a (slightly) unique message should appear in the JavaScript Console.

But of course, if we want to modify the list of players, we need to do that from inside the “App” component, since the “players” array exists inside the state object of that component.

Inside the “App” component, create a method named “removePlayer”:

removePlayer = () => {
    console.log("You tried removing a player...");
}

…and pass this method into the “PlayerControls” component:

render(){
    return (
        <div>
            <AddPlayerForm addPlayer={this.addPlayer} />
            <PlayerList
                players={this.state.players}
                selectedPlayer={this.state.selectedPlayer}
                selectPlayer={this.selectPlayer.bind(this)} />
            <PlayerControls
                selectedPlayer={this.state.selectedPlayer}
                changeScore={this.changeScore}
                removePlayer={this.removePlayer} />
        </div>
    );
}

Then pass it down one more level, from the “PlayerControls” component and into the “RemovePlayerButton” component:

return (
    <div>
        <button onClick={handleGiveClick}>Give 5 Points</button>
        <button onClick={handleTakeClick}>Take 5 Points</button>
        <RemovePlayerButton
            selectedPlayer={props.selectedPlayer}
            removePlayer={props.removePlayer} />
    </div>
);

You should now be able to trigger the “removePlayer” method from inside the “handleClick” function:

function RemovePlayerButton(props){
    function handleClick(){
        props.removePlayer();
    }
    return (
        <button onClick={handleClick}>Remove Player</button>
    );
}

Try clicking the “Remove Player” button to ensure the connection is working.

Next, we need the “removePlayer” method to accept an “id” argument, which will be the ID of the currently selected player:

removePlayer = (id) => {
    console.log(`The ID of the player you want to remove is ${id}.`);
}

…and to pass this ID to the method from inside the “RemovePlayerButton” component:

function RemovePlayerButton(props){
    function handleClick(){
        props.removePlayer(props.selectedPlayer);
    }
    return (
        <button onClick={handleClick}>Remove Player</button>
    );
}

This is basically all the “RemovePlayerButton” component needs to do, but as an additional touch, create a “isDisabled” variable, similar to what we created before:

function RemovePlayerButton(props){
    function handleClick(){
        props.removePlayer(props.selectedPlayer);
    }
    const isDisabled = props.selectedPlayer ? false : true;
    return (
        <button onClick={handleClick} isDisabled={isDisabled}>Remove Player</button>
    );
}

The difference is, this ternary operator checks to see if the “selectedPlayer” prop is null. If it is null, that means a player is not currently selected, which means the “Remove Player” button should be disabled.

Next, return to the “removePlayer” method:

removePlayer = (id) => {
    console.log(`The ID of the player you want to remove is ${id}.`);
}

Inside this method, we’re going to use two methods from the Lodash library to make removing a player from the state object a lot easier.

To begin, we’ll use the find method, which will find an object in an array, based on whatever properties and values we pass into it:

removePlayer = (id) => {
    const foundPlayer = _.find(this.state.players, { 'id': id });
}

Here, this find method accepts two arguments: the data that you want to search through and what you want to search for. In this case, we’re searching through the “players” array inside the state object, and we’re looking for an object that has an “id” property equal to the currently selected player.

You can see what this code is doing with a console.log statement:

removePlayer = (id) => {
    const foundPlayer = _.find(this.state.players, { 'id': id });
    console.log(foundPlayer);
}

If you select a player and click the “Remove Player” button, the object of that player will now appear inside the JavaScript Console.

To remove the selected player from the state object, we can use the without method from the Lodash library:

removePlayer = (id) => {
    const foundPlayer = _.find(this.state.players, { 'id': id });
    const updatedPlayers = _.without(this.state.players, foundPlayer);
}

The without method takes two arguments: the first argument is an array and the second argument is the object or value you want to remove from the array. In this case,

The important thing to note about the without method is that it creates a new array, rather than attempting to overwrite the state object.

For the updated array to be reflected within the interface though, we need to use the setState method:

removePlayer = (id) => {
    const foundPlayer = _.find(this.state.players, { 'id': id });
    const updatedPlayers = _.without(this.state.players, foundPlayer);
    this.setState({ players: updatedPlayers });
}

With this code in place, users will now be able to select a player and click the “Remove Player” button to remove them from the list.

Could this same functionality be achieved without the Lodash library?

Absolutely.

But considering how little code we’ve had to write, I think most people would agree that it’s worth using Lodash.

Refactoring

You’re probably starting to notice that the “index.js” file is growing a little cumbersome to manage. There’s a lot of code shoved into one place, which isn’t the most sustainable approach when developing an interface.

With this in mind, we’re going to start restructuring and refactoring our code. There’s still a few features we’re going to add after this process, but it helps if we clean everything up before we add anything new.

Restructuring Components

Right off the bat, it’s worth noting that the React library doesn’t require that we structure our projects in any particular way. You can use as many (or as few) files and folders to structure your project as you like, and you can follow whatever rules or conventions you find appropriate to our project.

In this section, we’re going to keep the structure of our project relatively simple, but I will share a handful of resources for you to check out if you’d like structural advice for more complicated projects.

To begin, create a separate JavaScript file inside the src folder for each of the components in our project.

These are the files you’ll need to create:

You’ll notice that these files are named in the pascal case format. This is the convention for naming files that contain a component. If a file does not contain a component, the name should remain in lowercase.

Next, add the following import statement to every component file:

import React from 'react';

Why do we need to import the React library into every component on an individual basis?

Because it’s ensures that each component can behave independently as its own, modular unit. If we want to share a component with another developer, for instance, there won’t be any confusion about what the component depends on since all of its specific dependencies are clearly outlined in the file.

Next, move all of the components from the “index.js” into the “App.js” file, leaving behind an “index.js” file that looks like this:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

ReactDOM.render(<App />, document.getElementById('root'));

Because we’ve moved the “App” component to a separate file, the interface will break, but that’s what we’ll fix right now.

Return to the “App.js” file and add the following line to the bottom of the file:

export default App;

Here, we’re using this export keyword to export the “App” component from the “App.js” file. By using the export keyword, we’re then to import the “App” component from another file. What this means is that, whenever you create a separate file for a component, you need to use this export keyword at the bottom of that file if you want that component to be usable from elsewhere.

As for the default keyword, this is the keyword we use when a file only contains a single component that we want to export. When working with React, you should always use the default keyword after the export keyword.

Next, return to the “index.js” file and import the “App” component using the import keyword:

import React from 'react';
import ReactDOM from 'react-dom';
import _ from 'lodash';
import App from './App'
import './index.css';

ReactDOM.render(<App />, document.getElementById('root'));

Here, we’re importing the component named “App”. It’s important to note that the name we’re using in the import statement must match the name defined in the component’s export statement.

This name is how we then refer to the component from inside the “index.js” file.

If we wanted to change the name of the “App” component, we’d have to change the name in at least three places:

  1. Inside the “App.js” file, where the class is being defined.
  2. Inside the “App.js” file, where the export statement is used.
  3. Inside the “index.js” file, inside the render method.

It’s standard practice to name a component’s file after the component, but this isn’t strictly necessary. You could name “App.js” whatever you like.

It’s worth noting that, when we refer to the location of the “App.js” file in the import statement, we use the “./” characters to clarify that the file is in the current directory (the src directory). We’re also able to omit the file extension, as a “js” extension will be assumed by default.

Based on these changes, the project will no longer be broken, but we haven’t really streamlined the project’s structure. We’ve just moved most of the code to the “App.js” file.

To remedy this, move all of the components to their associated files. This means you should:

Then, inside each file, include the relevant export statement:

export default ComponentName;

But since most of these components rely on other component, we also need to import them with import statements.

Inside the “App.js” file, we need to import the “AddPlayerForm”, “PlayerList”, and “PlayerControls” components:

import AddPlayerForm './AddPlayerForm';
import PlayerList './PlayerList';
import PlayerControls './PlayerControls';

Inside the “PlayerControls.js” file, we need to import the “RemovePlayerButton” component:

import RemovePlayerButton './RemovePlayerButton';

Inside the “PlayerList.js” file, we need to import the “PlayerListItem” component:

import PlayerListItem './PlayerListItem';

Finally, we need to import the Lodash library into the “App.js” and “PlayerList.js” files, as both of these components rely on it:

import _ from 'lodash';

You can also remove this same import statement from the “index.js” file, as the library is not specifically needed in that file.

Based on these changes, the project will work the same as it did before, but now we’ll have an easier time making sense of the code.

Refactoring Components

As we’ve talked about before, one of the benefits of building an interface with components is the way it allows us to avoid repetition. We can create components and then reuse them in multiple places.

But if we take a look at the “PlayerControls” component, we can see that, even in its relatively simple state, there’s a bit of redundancy:

function PlayerControls(props){
    function handleGiveClick(){
        props.changeScore(props.selectedPlayer, 5);
    }
    function handleTakeClick(){
        props.changeScore(props.selectedPlayer, -5);
    }
    return (
        <div>
            <button onClick={handleGiveClick}>Give 5 Points</button>
            <button onClick={handleTakeClick}>Take 5 Points</button>
            <RemovePlayerButton
                selectedPlayer={props.selectedPlayer}
                removePlayer={props.removePlayer} />
        </div>
    );
}

Here, we have these “handleGiveClick” and “handleTakeClick” methods, which both do basically the same thing – they just have different values passed into their “changeScore” methods. We also have two button elements inside the render method that are also very similar.

To improve upon this, create a new component inside a file named “GiveTakeButton.js”:

import React from 'react'

function GiveTakeButton(){
    // code goes here
}

export default GiveTakeButton;

…and return a button element from inside this component:

import React from 'react'

function GiveTakeButton(){
    return (
        <button></button>
    );
}

export default GiveTakeButton;

Then create a “handleClick” function that will be executed when a user clicks on this button element:

import React from 'react'

function GiveTakeButton(){
    function handleClick(){
        console.log("You clicked the button");
    }
    return (
        <button onClick={handleClick}></button>
    );
}

export default GiveTakeButton;

Next, return to the “PlayerControls” component and import this component with the following import statement:

import GiveTakeButton from './GiveTakeButton'

Then remove the methods for handling clicks from this component:

function PlayerControls(props){
    return (
        <div>
            <button onClick={handleGiveClick}>Give 5 Points</button>
            <button onClick={handleTakeClick}>Take 5 Points</button>
            <RemovePlayerButton
                selectedPlayer={props.selectedPlayer}
                removePlayer={props.removePlayer} />
        </div>
    );
}

…and replace the button elements with a pair of “GiveTakeButton” components:

function PlayerControls(props){
    return (
        <div>
            <GiveTakeButton />
            <GiveTakeButton />
            <RemovePlayerButton
                selectedPlayer={props.selectedPlayer}
                removePlayer={props.removePlayer} />
        </div>
    );
}

Both of these components will need access to the ID of the selected player and the “changeScore” method, so pass both of these values through as props:

function PlayerControls(props){
    return (
        <div>
            <GiveTakeButton
                    selectedPlayer={props.selectedPlayer}
                    changeScore={props.changeScore} />
            <GiveTakeButton
                    selectedPlayer={props.selectedPlayer}
                    changeScore={props.changeScore} />
            <RemovePlayerButton
                selectedPlayer={props.selectedPlayer}
                removePlayer={props.removePlayer} />
        </div>
    );
}

The component will also need a “points” prop, which will define how many points that button should give or take from a player:

function PlayerControls(props){
    return (
        <div>
            <GiveTakeButton
                points={5}
                selectedPlayer={props.selectedPlayer}
                changeScore={props.changeScore} />
            <GiveTakeButton
                points={-5}
                selectedPlayer={props.selectedPlayer}
                changeScore={props.changeScore} />
            <RemovePlayerButton
                selectedPlayer={props.selectedPlayer}
                removePlayer={props.removePlayer} />
        </div>
    );
}

Here, we’re wrapping these numbers in curly braces to ensure that they’re passed through as raw integers.

Back inside the “GiveTakeButton” component, ensure that the component can receive these props by placing the “props” keyword between the parentheses:

function GiveTakeButton(props){

    function handleClick(){
        console.log("You clicked the button");
    }

    return (
        <button onClick={handleClick}></button>
    );

}

Then modify the “handleClick” function so it will execute the “changeScore” method, using the ID and the points values that have been passed into it:

function GiveTakeButton(props){

    const changeScore = props.changeScore;
    const selectedPlayer = props.selectedPlayer;
    const points = props.points;

    function handleClick(){
        changeScore(selectedPlayer, points);
    }

    return (
        <button onClick={handleClick}></button>
    );

}

Here, I’ve also defined the “changeScore”, “selectedPlayer”, and “points” variables to make the code look a little cleaner. In this next section, we’ll see how to simplify these variable declarations even further.

Based on this code, the buttons will work as expected, but neither of them have any text inside of them.

To remedy this, write a conditional that checks the value of the “points” variable and figures out whether or not the button should start with the word “Give” or “Take”:

function GiveTakeButton(props){

    const changeScore = props.changeScore;
    const selectedPlayer = props.selectedPlayer;
    const points = props.points;

    function handleClick(){
        changeScore(selectedPlayer, points);
    }

    const text;
    if(points > 0){
        text = `Give ${points} Points`;
    } else {
        text = `Take ${points} Points`;
    }

    return (
        <button onClick={handleClick}></button>
    );

}

With this code in place, we can simply reference the “text” variable from inside the JSX code:

return (
    <button onClick={handleClick}>{text}</button>
);

But the problem with this code is that the “Take” button will say something like “Take -5 Points”, when what we really want it to say is “Take 5 Points”, without the minus symbol.

To fix this, place a minus symbol before the second instance of the “points” variable:

const text;
if(points > 0){
    text = `Give ${points} Points`;
} else {
    text = `Take ${-points} Points`;
}

This will convert a negative integer into a positive integer.

Alternatively, we could use the following syntax to write even less code:

const label = (points > 0) ? "Give" : "Take";
const text = `${label} ${Math.abs(points)} Points`;

Here, we’re using a ternary operator to determine whether the word “Give” or “Take” should appear on the button. We’re also using the Math.abs function to ensure that the value of “points” is always shown as a positive integer.

With these changes in place, this is what the code looks like:

function GiveTakeButton(props){

    const changeScore = props.changeScore;
    const selectedPlayer = props.selectedPlayer;
    const points = props.points;

    function handleClick(){
        changeScore(selectedPlayer, points);
    }

    const label = (points > 0) ? "Give" : "Take";
    const text = `${label} ${Math.abs(points)} Points`;

    return (
        <button onClick={handleClick}>{text}</button>
    );

}

This is actually more lines of code than we had earlier, but the code is much more flexible and it allows us to modify both of the buttons in one place, rather than treating the button elements as two separate elements. It’s also much easier to look at this component and see how it behaves at a glance.

Destructuring

Throughout our project, we have statements like this:

const changeScore = props.changeScore;
const selectedPlayer = props.selectedPlayer;
const points = props.points;

Here, we’re assigning variables to our props, which makes them easier to refer to throughout our component. It’s also handy to be able to see a quick list of what data a component can work with.

But this process does seem a little repetitive – especially since we’re always naming the variables after the names of the props.

Is there, perhaps, a more streamlined way of achieving the same thing?

Absolutely.

What we can do is use a feature of ES6 known as destructuring, and the best way to understand this feature is to simply use it.

Inside the “GiveTakeButton” component, replace the above lines with the following statement:

const { changeScore, selectedPlayer, points } = props;

Can you see what we’ve done?

Here, we’re using curly braces to extract the “changeScore”, “selectedPlayer”, and “points” props from the props object. These values then become available as variables throughout the rest of the component.

In other words, these two snippets are identical:

// Not Destructured
const changeScore = props.changeScore;
const selectedPlayer = props.selectedPlayer;
const points = props.points;

// Destructured
const { changeScore, selectedPlayer, points } = props;

Obviously though, the latter example is much cleaner.

It’s worth noting that the same thing can be achieved inside a class component. This, for instance, is how we’d destructure the props object inside the “PlayerListItem” component:

// Not Destructured
const id = this.props.id;
const name = this.props.name;
const score = this.props.score;
const selectedPlayer = this.props.selectedPlayer;

// Destructured
const { id, name, score, selectedPlayer } = this.props;

But when working with a functional component, such as the “GiveTakeButton” component, we can take this even further.

For example, when defining a functional component, we can replace the “props” parameter (between the parentheses) with a list of whatever values we want to extract from that props object:

function GiveTakeButton({ changeScore, selectedPlayer, points }){

    function handleClick(){
        changeScore(selectedPlayer, points);
    }

    const label = (points > 0) ? "Give" : "Take";
    const text = `${label} ${Math.abs(points)} Points`;

    return (
        <button onClick={handleClick}>{text}</button>
    );

}

In the same way as before, these values will then become available throughout the component as variables.

Prop Types

The interface we’ve built so far is simple enough that it’s not difficult to keep track of what components we’ve created, what data is being passed between these components, and how we’re supposed to use each of our components.

For example, this is how we use the “GiveTakeButton” component:

<GiveTakeButton
    points={-5}
    selectedPlayer={props.selectedPlayer}
    changeScore={props.changeScore} />

But imagine what would happen if we forgot to pass through these props:

<GiveTakeButton />

The application would break and it might not be completely obvious what we’ve forgotten to do.

You might not expect to forget to pass through these props, but consider the following scenarios:

In these cases, it’s not difficult to imagine that you might need a hand in ensuring that you’re using the components correctly.

To accommodate for this, we can use what is known as type-checking. This is where we write code that verifies that the data being passed into a component is the type of data that we expect. For example, we can ensure that, if a prop needs to be a number, then the component will only accept a number. We can also ensure that a prop is required, meaning an easily understood error will be thrown if we forget to pass a certain prop into the component.

To use type-checking with React, write the following block of code above or below – not inside of – the “GiveTakeButton” component:

GiveTakeButton.propTypes = {
    // rules go here
}

Here, we’re defining this propTypes object and attaching it to the “GiveTakeButton” component.

Inside the propTypes object, define a validation for the “points” prop:

GiveTakeButton.propTypes = {
    points: React.PropType.number
}

Here, we’re telling React that we expect the “points” prop to be a number.

To see what happens if we don’t pass a number into the “points” prop, try passing a string into the component:

<GiveTakeButton
    points="5"
    selectedPlayer={props.selectedPlayer}
    changeScore={props.changeScore} />

If you click this button, an error will appear in the JavaScript Console:

Warning: Failed prop type: Invalid prop points of type string supplied to GiveTakeButton, expected number.

You’ll notice that, right away, the error is extremely easy to understand, allowing us to quickly figure out what we’ve done wrong.

There’s a lot of prop types we can validate against, but most of the time, you’ll be validating against JavaScript primitives:

In the case of the “GiveTakeButton” component, we want to ensure that the “points” and “selectedPlayer” props are numbers, while the “changeScore” prop is a function:

GiveTakeButton.propTypes = {
    points: React.PropTypes.number,
    selectedPlayer: React.PropTypes.number,
    changeScore: React.PropTypes.func
};

…and because this component won’t work without any of these proprs, we can also attach “isRequired” to the end of our validations:

GiveTakeButton.propTypes = {
    points: React.PropTypes.number.isRequired,
    selectedPlayer: React.PropTypes.number.isRequired,
    changeScore: React.PropTypes.func.isRequired
};

With this code in place, if we forget to define the “points” prop:

<GiveTakeButton
    selectedPlayer={props.selectedPlayer}
    changeScore={props.changeScore} />

…an extremely clear message will appear in the JavaScript Console:

Failed prop type: The prop points is marked as required in GiveTakeButton, but its value is undefined.

It’s worth noting that, if you don’t care what type of data is passed into a component, but a prop is nevertheless required, you can check for a prop type of “any”:

points: React.PropTypes.any.isRequired

This isn’t appropriate for our purposes, but it’s handy to know for future use.

Default Props

What’s also useful is the ability to define default props. By defining a default prop, it doesn’t matter if you (or someone else) forgets to define a prop when embedding the component, since it will use the default value.

To define default props for the “GiveTakeButton” component, add the following code above or below – but not inside – the component:

GiveTakeButton.defaultProps = {
    // defaults go here
};

Within this “defaultProps” object, we can pass through the name of a prop and the value that this prop should have by default.

For example, this is how we’d ensure that the “points” prop always has a value:

GiveTakeButton.defaultProps = {
    points: 5
};

With this code in place, if we forget to define the “points” prop:

<GiveTakeButton
    selectedPlayer={this.props.selectedPlayer}
    changeScore={this.props.changeScore} />

…the component will simply assume the “points” props is equal to 5.

In this case, I would rather require the “points” prop rather than set a default value, as there’s no default value that would be appropriate in most situations, but for circumstances where a certain value will be accurate most of the time, default props can make your components easier to work with.

Styling

React has a lot of standardized ways of handling things, but one thing the community is still split on is the matter of adding styles to components.

So far, we’ve taken a relatively familiar approach to styling: we’ve created a file named “index.css”, which contains both of our styling rules, and we’ve included that file using an import statement inside the “index.js” file.

This is what the stylesheet looks like:

.player-list-item:hover{
  cursor:pointer;
}

.selected{
  background-color: yellow;
}

The fact that we’ve included a stylesheet from inside JavaScript code is a little weird, but it’s not radically different from embedding it inside a HTML page.

There is, however, a different way of approaching this.

What we can do is define our styles within our components. This means we don’t just embed our stylesheets inside our JavaScript code. Instead, we write our stylesheets inside our JavaScript code.

To understand why we would do such a thing, consider that, traditionally, developers have separate the structure of an interface and the styles of an interface. They put the HTML code in one file and the CSS code in another file. This makes sense when the structure of a website is relatively predictable and static, but React is neither of these things. Everything in a React project is a component, which means everything in a React project can be moved around, dropped into other projects, and modified without breaking the rest of the application – or at least, that’s the ideal. Using stylesheets, however, makes all three of these things difficult, especially if the look of your component is closely tied to how that component behaves.

For example, imagine if we created a component named “Loading” that we used to display a progress bar as data is being loaded into a page. This exactly the sort of component that derives its functionality from its looks. With this in mind, it’s not difficult to imagine that, if we created a handful of rules in a separate stylesheet for this component, the connection between that component and the stylesheet might break sooner or later. The only way to ensure the styling doesn’t break is if we always keep track of where the styles are and whether or not they’ve been imported into the component.

In a small project, this might not seem like a big deal, but once you start working with dozens of components and potentially hundreds of rules inside a stylesheet, the alternative approach can be a lot more enticing.

Creating Styles

In React, styles are defined as JavaScript objects.

To see how to define a style for a component, switch to the “PlayerListItem” component, and above the return statement create a variable named “styles”:

const styles = {
    // styles go here
}

It doesn’t matter what we call this variable. If we wanted to create multiple styles inside a single component, it’d make sense to use a less generic name, but since we only need a single style, this “styles” name will be fine.

Inside this object, define a property named “backgroundColor”, and set this property to a value of “yellow”:

const styles = {
    backgroundColor: 'yellow'
}

Here, we’ve created a style that is equivalent to the following:

{
    background-color: yellow;
}

…and as we can see, defining a style in JavaScript is similar to defining a style in CSS. There are, however, a couple of major differences:

  1. We use the camel case format to define the property names, rather than using lowercase names with hyphens.
  2. When defining a value as a string, we need to wrap that string in a pair of quotation marks. If we didn’t include quotation marks, our code would try to find a variable named “yellow” (which, in this case, it wouldn’t find).

It’s also worth noting that we don’t end each line in our “styles” object with a semi-colon, as we do when writing regular CSS. Instead, we separate each line with a comma, as is usual inside a JavaScript object.

To actually use this style, remove the className attribute from the li element:

<li onClick={handleClick}>
  {name}: {score}
</li>

…and replace it with a style attribute that is equal to the name of the “styles” object that we’ve defined:

<li style={styles} onClick={handleClick}>
  {name}: {score}
</li>

Take note that the attribute is called style, not styles. It’s singular.

It’s worth noting that you can use the className and style attributes on a single element, but it’s usually best if you choose one or the other. Mixing the two approaches will only make your component more difficult to manage.

Stylesheet Logic

So far, this is the code we’ve been using to highlight players after the user has selected them:

const selectedClass = (selectedPlayerId === id) ? 'selected' : null;

But since we’re not relying on a class named “selected” anymore, we need to figure out how to integrate this logic with our new “styles” object.

What’s particularly neat though is that, because our styling is defined inside a regular a JavaScript object, we can define logic inside the object itself.

To see what I mean, change this:

const styles = {
    backgroundColor: 'yellow'
}

…to this:

const styles = {
    backgroundColor: (selectedPlayerId === id) ? 'yellow' : null;
}

Here, we’re using the same conditional from before, but instead of returning a string of “selected” when a player is selected, we’re returning a string of “yellow”. This recreates the functionality of the “selected” class, but now that functionality is completely contained within the component itself. This means the look and functionality of the component is all contained within a single file, making it easier to manage from this point onward.

Default Styles

We know for certain that we want the background color of a player’s li element to change when that player is selected, but we can’t be sure that we necessarily what that background color to be yellow. With this in mind, it doesn’t make a lot of sense to hard-code “yellow” value into the component.

This is a scenario where default props can be quite useful.

Either above or beneath the “PlayerListItem” component – but not inside the component – create a “defaultProps” object:

PlayerListItem.defaultProps{
    // props go here
}

…and define a prop named “highlightColor”, which should be set to a value of “yellow”:

PlayerListItem.defaultProps{
    highlightColor: 'yellow'
}

Then modify the “styles” object so it references this “highlightColor” prop, rather than referencing the hard-coded value:

const styles = {
    backgroundColor: (selectedPlayerId === id) ? props.highlightColor : null;
}

Because of this code, the “PlayerListItem” component is that little bit more flexible. By default, it will grab the value of the “highlightColor” prop, but if we want to change this value, we can pass a different color in as a prop:

<PlayerListItem
    key={player.id}
    id={player.id}
    name={player.name}
    score={player.score}
    selectedPlayer={selectedPlayer}
    selectPlayer={selectPlayer}
    highlightColor="#ff0099" />

It’s a small change, but nevertheless makes the component particularly easy to work with.

Conclusion