Skip to main content

React from the Beginning

Course Notes Outline

Reference

create-react-app docs outline

React.Component (docs reference in a nutshell)

See React.Component in the docs for all the gory details. Below is a modest attempt to provide just a nuts and bolts reference for ease of use (i.e., consult the actual docs for examples of everything below).

Each component has several "lifecycle methods" that you can override to run code at particular times in the process. You can use this lifecycle diagram as a cheat sheet. In the list below, commonly used lifecycle methods are marked as bold. The rest of them exist for relatively rare use cases.

Conditional rendering cheatsheet

This article gives a very nice overview of conditional rendering in React. The author specifies the following waysof conditional rendering: if, if else, ternary, switch case, multiple conditional renderings in React, nested conditional rendering in React, conditional rendering with higher-order components, and finally if else components. Here's the actual cheatsheet:

  • if
    • most basic conditional rendering
    • use to opt-out early from a rendering (guard pattern)
    • cannot be used within return statement and JSX (except self invoking function)
  • if-else
    • use it rarely, because it's verbose
    • instead, use ternary operator or logical && operator
    • cannot be used inside return statement and JSX (except self invoking function)
  • ternary operator
    • use it instead of an if-else statement
    • it can be used within JSX and return statement
  • logical && operator
    • use it when one side of the ternary operation would return null
    • it can be used inside JSX and return statement
  • switch case
    • avoid using it, because it's too verbose
    • instead, use enums
    • cannot be used within JSX and return (except self invoking function)
  • enums: multiple conditional renderings
    • use it for conditional rendering based on multiple states
    • perfect to map more than one condition
  • nested conditional rendering
    • avoid them for the sake of readability
    • instead, split out components, use if statements, or use HOCs
  • conditional rendering with higher-order components
    • components can focus on their main purpose
    • use HOC to shield away conditional rendering
    • use multiple composable HOCs to shield away multiple conditional renderings
  • external templating components: if else components
    • avoid them and be comfortable with JSX and JS

React Router (docs reference)

The following are a few of the helpful links to get started with React Router:

Now for the actual docs reference:

Redux (docs reference)

Here is the homepage for Redux and here is the GitHub repo. And here are docs links for reference:

React-Redux (docs reference)

Here is the homepage for React Redux and here is the GitHub repo. And here are docs links for reference:

Hooks API (docs reference)

React practical tutorial highlights (Tic-Tac-Toe)

JSX accommodates the use of any JavaScript expression

As noted in the docs:

JSX comes with the full power of JavaScript. You can put any JavaScript expressions within braces inside JSX. Each React element is a JavaScript object that you can store in a variable or pass around in your program.

Check out this post for a somewhat deep dive into expressions versus statements in JavaScript and this post for a more trimmed version.

Here's the gist:

Statement: A statement is a piece of code that can be executed and performs some kind of action. For example, if is a statement:

let myStr;
if (myBool) {
myStr = 'Yes';
} else {
myStr = 'No';
}

One more example of a statement: a function declaration.

function twice(x) {
return x + x;
}

Expression: An expression is a piece of code that can be evaluated to produce a value. For example, the code between the parentheses is an expression:

let myStr = (myBool ? 'Yes' : 'No');

The operator ? used between the parentheses is called the ternary operator. It is the "expression version" of the if statement.Let's look at more examples of expressions. We enter expressions and the REPL evaluates them for us:

> 'ab' + 'cd'
'abcd'
> Number('123')
123
> true || false
true

What is allowed where? The current location within JavaScript source code determines which kind of syntactic constructs you are allowed to use:

  • The body of a function must be a sequence of statements:
function max(x, y) {
if (x > y) {
return x;
} else {
return y;
}
}
  • The arguments of a function call or a method call must be expressions:
console.log('ab' + 'cd', Number('123'));

However, expressions can be used as statements. Then they are called expression statements. The opposite is not true: when the context requires an expression, you can't use a statement.

The following code demonstrates that any expression bar() can be either expression or statement – it depends on the context:

function f() {
console.log(bar()); // bar() is expression
bar(); // bar(); is (expression) statement
}

Expressions and statements in React: As noted above, there is a difference between expressions and statements in JavaScript. In the context of React, specifically as it concerns the usage of JSX, we are only permitted the use of JavaScript expressions within JSX. Per the discussion above, we could not use an if statement in JSX in React. But we could use its "expression equivalent" in the ternary operator ?, and we will see this being done very frequently.

React 101

Starter notes (NodeJS, create-react-app, React docs, etc.)

As we will see, NodeJS is not necessary in order to use React, but you will be in for a world of pain if you don't use NodeJS. It will make your life so much easier. In particular, once we have made sufficient progress, we will be able to use the create-react-app CLI and add on numerous other packages meant for React via Node. It's really the only way to go.

As noted at the top of this file, there are several React-specific docs to make development with React as painless as possible. Use the docs to your advantage!

What React is and why we need it

Before we get into the weeds as to how to use React, it would be a good idea to know what it is, where it comes from, what problems it has tried to solve, etc. The more background we can have on it before directly using it the better. Our background knowledge can inform our use.

In web development, 1995 was a monster year. Python (really 1991), Java, PHP, Ruby, Apache, and (prematurely) JavaScript were born. Netscape (now Mozilla) had Navigator (now Firefox) as the only real web browser at that point. They know Microsoft is coming to build their own browser so the CEO, Marc Andreessen, hires Brendan Eich to build a scripting language. That language is not meant to compete with the heavy lifters (i.e., Python, Java, PHP, Ruby, etc.) because those heavy lifters are already handling all of the back-end stuff. We want a scripting language that's accessible to amaeteurs and hobbyists and people who just want to dabble in basic programming. Why? Why not just make it a full-on language with inheritance and other features like those mentioned above? There are many reasons, but the main one is probably that in 1995 there's a grand total of about 23,000 websites. That may sound like a decent amount, but it's nothing at all--today we have billions of websites. So the web was in its absolute infancy in 1995. You're not going to be able to get heavy-lifter developers to migrate over to the JavaScript world since the ecosystem is so small at that point. And websites were mostly inert. Think Wikipedia. You open the page and that's it.

JavaScript came into the world to be the easy programming language while the heavy-lifters remained as they were. Fast forward to roughly 2005 and AJAX comes out. We have iOS and Android. jQuery comes out in 2006. And we hit 100 million websites. So in just 10 years, we went from around 23,000 websites to over 100 million. People are now carrying around full computers in their pockets that are able to run JavaScript. AJAX has revolutionized the web because internet connections are faster so you can send lots of little pieces of data. And in all of this jQuery was awesome. In some ways, jQuery kind of unified the DOM almost as a language across everything that used the DOM. And jQuery was awesome because it took us from web pages that used hundreds of lines of JavaScript and condensed them down to just a few lines. So jQuery was great, but the web was growing at a frenetic pace.

If we hop to around 2010, then the V8 engine has come out, Node.js has come out, and Angular is born (and BackBone). This is where things really start to change for JavaScript. At this point in history, Instagram, Netflix, and Twitch go on the web, and browsers have gone from needing to serve up a tiny number of pages in 1995 to now where you have major websites like Netflix, Walmart, Amazon, etc. All of these websites have gone from being little fun marketing sites in 2000 to being really important parts of the company. They're not just little applications but major software as a service or they're a major platform in being able to actually make money.

Angular is the first UI framework, and a UI framework is a framework that seeks to simplify your life. So first we had JavaScript files that became unmanageable. jQuery was a lifesaver and condensed the code we needed. The web continued to explode and then the jQuery files got out of hand. Angular sought to reign in the chaos. In 2013 enters React, and React is almost unanimously seen as a vast improvement over Angular 1 (not necessarily Angular as it is now). It is also a UI framework.

Basically, a UI framework is a whole bunch of JavaScript someone else has written to try and make your life easier. In what way does it make life easier? React seeks to answer the following question: "How can we modernize web development?" Because we can't have thousands of really long files of JavaScript. It becomes intractable. And if you're Facebook then you'll have a ton of those really monolithic files. So it answers this question in the following way:

  • Modularize: It breaks up the application into a bunch of tiny little pieces. So once again the goal is to get back to the point where files are reasonably short and manageable. This is also good because it allows the files to be encapsulated. That is, we can follow some basic object-oriented programming principles and make our files easier to reuse. We can pass them around freely from one place to the next within our application (they manage their own data and all their methods are internal and so forth).
  • Manages state effectively: This is something that Angular did not really do. We'll talk about this a lot more later, but now you have webpages that are changing constantly in a big way, and you need someone who is in charge of it, and it's not a good approach to just let the DOM be the source of truth anymore.
  • Efficient: If you think about Facebook, then you can think about chats, notifications, messages, etc., all happening nearly instantly and all of those things are tiny DOM manipulations. And jQuery was computationally very very expensive in how it manipulated the DOM. React can do this in a very efficient way.
  • Front-end/back-end separation of concerns: React completely separates the front-end from the back-end. There are a number of positive benefits about this, but just to name a few: You can have two separate teams (front-end people and back-end people). The front-end people can focus entirely on React and the back-end people can focus on their own thing. In the past, if you had the front-end and back-end teams tightly coupled, as was the case for many many years, then if one thing went down the other went down as well. If you wanted to change one you had to change the other. Separating them out makes everything much more modular, easier to manage and maintain, etc.
  • Hardware increases: Your phone may have a stronger processor than, say, what's on your T2.micro on AWS. The hardware is just there now to where we want to offload as much as possible to the browser because the hardware that the user is running their browser on is outstanding, and for large-scale websites this can save so much processing power.
  • Declarative instead of imperative: We can get away from telling the computer exactly how to do something to simply say, "Hey, this is what I want you to do."

The super short version: What is React? React is a whole bunch of JavaScript that someone else wrote (mostly Facebook) that helps your development go from being boring, small, and unsophisticated to being big, exciting, professional, and organized. If you have a tiny website or project, then React is not what you need. React is made to build buildings and cities--it is not meant to make log cabins or tents. That's what the web was in the beginning. It is no longer that way. The web grew up. It is no longer boring with unsophisticated progammers. It is now professional programmers, large teams, huge companies (e.g., Amazon, Facebook, Google, etc.), and it allows you to make your UI (i.e., your front-end or your stuff inside the browser) really well.

First React program (and touching on JSX and Babel)

Without create-react-app, it's not exactly extremely straightforward to get an application going with React. Remember that React is meant to build large websites. Here is what you get when professionally developing with React:

  • React itself
  • React DOM (when using React for the web)
  • JSX (to make it easier to use declarative markup)
  • ES6 (to take advantage of modern JS)
  • Babel (to transpile modern JS and JSX into ES5 and lower JS all browsers can understand)
  • Webpack (for module bundling)
  • NodeJS (for too many things to count with create-react-app being first among many)

At the beginning, where we will start without using create-react-app, we could certainly go the Express route (where we statically load one big folder and then load up the index.html in whatever subfolder as we want), but we can just as well use the live server extension in VSCode to launch our local index.html file instead. Whatever is most comfortable.

No matter what we do, we will need the following through the React CDN links as well as cdnjs for babel-standalone:

  • React: <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
  • ReactDOM: <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
  • Babel: <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>

These links will give us access to React proper, ReactDOM (React can be used in contexts without a DOM, but we will be working in the context where there is a DOM), and Babel (babel-standalone is a standalone build of Babel for use in non-NodeJS environments.) The skeleton for our index.html file might look like the following:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First React Program</title>
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
</head>

<body>

<!-- What goes in here now? -->

</body>

</html>

What goes in the body? This is where the particulars of React will come into play. We will first have <div id="root"></div> which is to serve as the "root" of our application. This is where our content will get dumped. Then below this div we will want to drop a script tag with type="text/babel" so whatever we place within the script tag can be processed properly:

<body>

<div id="root"></div>

<script type="text/babel">
ReactDOM.render(
<h1>Sanity Check</h1>,
document.getElementById('root')
)
</script>

</body>

All in all we end up with the following most basic first React program:

Let's go through this in more detail than we did above:

1. React: This is React proper. It is React itself. You can see the non-minified version of React (it's only a couple thousand lines of code).

2. ReactDOM: This is ReactDOM. And we can see we make use of ReactDOM in our program by literally using ReactDOM and calling the render method on it. You can see the non-minified version of ReactDOM if you want (it's several thousand lines long). The heart of our program is lines 18-21 in the screenshot above, and this is where we make use of ReactDOM, specifically the render method. In our example we supplied render with two arguments:

  1. Some HTML (our h1 tag)
  2. A container (a div in our case which we selected using basic JavaScript)

In fact,the docs note the following syntax for render:

ReactDOM.render(element, container[, callback])

And we get the following basic description: "Render a React element into the DOM in the supplied container [...]. If the optional callback is provided, then it will be executed after the component is rendered or updated."

3. Babel: This is Babel. And we can see we make use of Babel in our program via the script tag with the attribute type="text/babel". You can see the non-minified version of Babel if you want (it's several thousand lines of code long). Check out babeljs.io to experiment and find out what it's really doing behind the scenes. As the website notes, Babel is a JavaScript compiler. We'll explore what this means further momentarily.

4. JSX: What is JSX? As always, the docs give us a clue, but the basic gist is this: JSX is a JavaScript (J) syntax (S) extension (X), hence the JSX name, and it is recommended to use with React so we can describe what the UI should look like (declarative vs. imperative). And JSX goes hand in hand with Babel which is also working with React.

What does all of this mean? Well, let's first explore what Babel does even without React first. It's a JavaScript compiler. It basically does two things:

  1. It makes the fancy new ES6+ JavaScript we use reverse-compatible so browsers can understand it (many browsers have not completely updated to support ES6+). As an example, something really simple like () => 2 in ES6+ speak would be turned into
(function () {
return 2;
});

which any browser can understand.

  1. The second thing Babel will do (if we have react checked in the left sidebar presets when trying it out) is transpile our JSX into code React proper can work with (this is where React itself comes into play). So something like () => <h1>Sanity Check</h1> gets transpiled by Babel into
(function () {
return React.createElement("h1", null, "Sanity Check");
});

As you can see, we are directly making use of React; specifically, we are making use of the createElement method and passing arguments to it. As the docs note, each JSX element is just syntactic sugar for calling React.createElement(). How does createElement work? The docs give us an example:

React.createElement(
type,
[props],
[...children]
)

The docs loosely note the following: This creates and returns a new React element of the given type. The type argument can be either a tag name string (such as 'div' or 'span'), a React component type (a class or a function), or a React fragment type. Code written with JSX will be converted (spoiler alert: this conversion is done using Babel!) to use React.createElement(). You will not typically invoke React.createElement() directly if you are using JSX. See React Without JSX to learn more.

Returning to our first program, our script tag on line 17 with type="text/babel" indicates that we want Babel to compile our code into something the browser can understand. If what we use in our script is just JavaScript, then it will simply make the JavaScript ES6+ reverse-compatible. But if it's JSX, then it will transpile our code into something React knows how to handle. In our own case, Babel took () => <h1>Sanity Check</h1> and turned this into

(function () {
return React.createElement("h1", null, "Sanity Check");
});

From above, we see that the type we gave it was an h1, null for [props] (we'll get to props and all that good stuff soon enough, but right now you can think of props as basically attributes on a normal HTML element), and "Sanity Check" for [...children]. Of course, we may have cases where we have a lot more than a single child. Consider something still rather basic but that could be a nightmare to deal with without Babel:

() => (
<div>
<h1>Heading</h1>
<h2>First Subsection</h2>
<p>
Little paragraph in subsection and we may link to <a href="google.com">Google</a> or something like that.
</p>
<p>Another <span>little</span> paragraph</p>
</div>
)

Babel will turn this into the following:

(function () {
return React.createElement("div", null, React.createElement("h1", null, "Heading"), React.createElement("h2", null, "First Subsection"), React.createElement("p", null, "Little paragraph in subsection and we may link to ", React.createElement("a", {
href: "google.com"
}, "Google"), " or something like that."), React.createElement("p", null, "Another ", React.createElement("span", null, "little"), " paragraph"));
});

You can easily see how children of the single div can have multiple children themselves and things can quickly spiral out of control.

More JSX and Babel

Facebook made JSX and you can see the GitHub repository for JSX if you're really into that. As they note in the description for the repository: The JSX specification is a XML-like syntax extension to ECMAscript. JSX is made basically for React. Because using React without JSX quickly becomes impossible as the end of the note above started to hint towards but which we will quickly see even more soon.

Returning to the basics, we see that Babel will take something like

<div id="root">I love React!</div>

and turn it into

React.createElement("div", {
id: "root"
}, "I love React!");

So what looks like HTML to us is not that at all when Babel is looking at it (especially in the context of using React). It's pure JavaScript. We could maybe add a class to our HTML element like so:

<div id="root" class="container">I love React!</div>

But we cannot do this! Why? Because class is a keyword in JavaScript. In fact, Babel will take a silly class like

class Car {
constructor(color, mileage) {
this.color = color;
this.mileage = mileage;
}

showMileage() {
return this.milage;
}
}

and compile it into

var Car = function () {
function Car(color, mileage) {
_classCallCheck(this, Car);

this.color = color;
this.mileage = mileage;
}

_createClass(Car, [{
key: "showMileage",
value: function showMileage() {
return this.milage;
}
}]);

return Car;
}();

Hence, in React, we do not use class for a class attribute we might normally put on an HTML element. Instead, we use className:

<div id="root" className="container">I love React!</div>

And Babel turns this into

React.createElement("div", {
id: "root",
className: "container"
}, "I love React!");

So every attribute that we add in JSX will be added as a prop(erty) in the second argument to React.createElement. Another "gotcha" to remember in React is we always need to close our elements even if they may be conventionally self-closing. For example, we need <br /> not <br>, <img /> not <img>, etc.

We can create a more interesting React program (though still tiny in the grand scheme of things) in the following manner:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First React Program</title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
</head>

<body>

<div id="root"></div>

<script type="text/babel">

const markup = <div className="row">
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src="https://picsum.photos/400/400/" />
</div>
<div className="card-content">
<p>React From the Beginning</p>
<p>Robert Bunch</p>
</div>
<div className="card-action">
<a href="#">$9.99</a>
</div>
</div>
</div>
</div>

ReactDOM.render(
markup,
document.getElementById('root')
)
</script>

</body>

</html>

Not the addition of the materialize link: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">. Of course, the real addition is the markup variable. The entire thing looks like HTML, but it isn't. It's JSX. If we throw everything in the markup variable into Babel, we will see what is really being done underneath the hood:

React.createElement("div", {
className: "row"
}, React.createElement("div", {
className: "col s2"
}, React.createElement("div", {
className: "card hoverable small"
}, React.createElement("div", {
className: "card-image"
}, React.createElement("img", {
src: "https://picsum.photos/400/400/"
})), React.createElement("div", {
className: "card-content"
}, React.createElement("p", null, "React From the Beginning"), React.createElement("p", null, "Robert Bunch")), React.createElement("div", {
className: "card-action"
}, React.createElement("a", {
href: "#"
}, "$9.99")))));

What a mess! Imagine having to do this every single time instead of just using JSX. The real power of all of this isn't just the fact that Babel does a bunch of compiling/transpiling underneath the hood. The power comes from how modular everything can be when we note that all of this is just JavaScript. So we can make variables and the like, perform computations, etc., and place the results in our JSX dynamically. The result can eventually be a bunch of dynamically rendered HTML. So how do we insert variables and the like into JSX?

A decent way of thinking about JSX is that it is in "HTML mode" by default, where nothing is dynamic. But we can use curly braces { javascrpt-mode } to enter "JavaScript mode." So when Babel gets to a curly brace it expects whatever is inside to be an expression in JavaScript that can be evaluated. The expression is evaluated and placed and when the closing curly brace is encountered, HTML mode resumes. So when Babel encounters something like {title} it knows you mean the variable title as opposed to the string 'title'. We can run almost anything we want inside of the curly braces except for a full-blown statement like an if...else statement, a for loop, etc. But this is also where the power of the ternary ? in JavaScript comes into play. We can make a more dynamic HTML file like the following:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First React Program</title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
</head>

<body>

<div id="root"></div>

<script type="text/babel">

const title = 'React From the Beginning';
const name = 'Robert Bunch';
// const saleOn = false;
function saleOn() {
return true;
}

const markup = <div className="row">
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src="https://picsum.photos/400/400/" />
</div>
<div className="card-content">
<p>{title}</p>
<p>{name}</p>
</div>
<div className="card-action">
<a href="#">${saleOn() ? 9.99 : 59.99}</a>
</div>
</div>
</div>
</div>

ReactDOM.render(
markup,
document.getElementById('root')
)
</script>

</body>

</html>

If we dumped everything within the script tags in the body, we would end up with the following via Babel:

var title = 'React From the Beginning';
var name = 'Robert Bunch';

function saleOn() {
return true;
}

var markup = React.createElement("div", {
className: "row"
}, React.createElement("div", {
className: "col s2"
}, React.createElement("div", {
className: "card hoverable small"
}, React.createElement("div", {
className: "card-image"
}, React.createElement("img", {
src: "https://picsum.photos/400/400/"
})), React.createElement("div", {
className: "card-content"
}, React.createElement("p", null, title), React.createElement("p", null, name)), React.createElement("div", {
className: "card-action"
}, React.createElement("a", {
href: "#"
}, "$", saleOn() ? 9.99 : 59.99)))));
ReactDOM.render(markup, document.getElementById('root'));

Yikes! Definitely use JSX. Apart from the ease, JSX also prevents injection attacks. So it is safe to embed user input in JSX.

Understanding React elements as opposed to DOM elements

What is a React element? The docs note that an element describes what you want to see on the screen:

const element = <h1>Hello, world</h1>;

And that unlike browser DOM elements, React elements are plain objects and are cheap to create. React DOM takes care of updating the DOM to match the React elements. To get a sense of how much cheaper it is to create a React element than a DOM element, consider the following basic HTML file:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sample React Element</title>

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
</head>

<body>

<div id="root"></div>

<script type="text/babel">

const element = <h1 id="sample-react-element">Hello, world</h1>;

console.log('This is a React element:');
console.dir(element);
console.log('============================================================');
console.log('========== React element above; DOM element below ==========');
console.log('============================================================');

ReactDOM.render(
element,
document.getElementById('root')
)

const sampleElement = document.getElementById('sample-react-element');

console.log('This is a DOM element:');
console.dir(sampleElement);
</script>

</body>

</html>

What's happening here? First, we create a React element with the JSX const element = <h1 id="sample-react-element">Hello, world</h1>; which is converted to the following using Babel:

var element = React.createElement("h1", {
id: "sample-react-element"
}, "Hello, world");

Before this React element is created in the DOM, we display an interactive list of the properties of this object in the console via console.dir. After React has updated the DOM, by transforming our element into an actual DOM element by placing it in the DOM, we select our "now actual DOM element" by using basic JavaScript: const sampleElement = document.getElementById('sample-react-element');, and then we display an interactive list of the properties of our "now actual DOM element". The beginning of the output will look like this:

To fully appreciate the difference, watch what happens when you view and expand some of the React element's properties as opposed to the actual DOM element's properties:

Talk about the DOM element not being cheap! That object is huge with all sorts of properties on it. The React object is just a plain old JavaScript object (POJO) with only a few properties. It's when React injects it into the actual DOM that it becomes a real DOM element with all of the crazy extensive properties one might expect of a DOM element.

ReactDOM.render() and the virtual DOM

ReactDOM.render takes two arguments (really three, with the third one being optional as a callback):

  1. What we want to render (i.e., a React element which is probably some JSX)
  2. Where we want to render it (this will be an actual DOM element, something that already exsists in our markup as of page load)

Let's return to our example from earlier where we were dropping some variables in our JSX:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First React Program</title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
</head>

<body>

<div id="root"></div>

<script type="text/babel">

const title = 'React From the Beginning';
const name = 'Robert Bunch';
// const saleOn = false;
function saleOn() {
return true;
}

const markup = <div className="row">
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src="https://picsum.photos/400/400/" />
</div>
<div className="card-content">
<p>{title}</p>
<p>{name}</p>
</div>
<div className="card-action">
<a href="#">${saleOn() ? 9.99 : 59.99}</a>
</div>
</div>
</div>
</div>

ReactDOM.render(
markup,
document.getElementById('root')
)
</script>

</body>

</html>

We can now start to see where the full power of React comes into play: On the first go around, React has to build out the entire DOM (of course, this is quite expensive, as we observed through the note above when we looked at actual DOM elements). BUT from here on out (i.e., after React has built and rendered the DOM on the initial page load), any time something inside of <div id="root"></div> changes, instead of it being an actual DOM element, it's a React element, and React in the background keeps track of the old as well of the new. So it compares the two objects. Again, they're not DOM objects. They're React elements which are regular JavaScript objects which are cheap and small. It compares the two and sees what's actually different.

Here's a clearer verbal description of what React does behind the scenes: When ReactDOM.render is first called, React builds the virtual DOM which consists only of React elements (i.e., plain old JavaScript objects or POJOs that are cheap and small). Then React builds out the actual DOM (which consists of all of the very expensive objects that have tons of properties and prototypes) from the virtual DOM it just created. Now suppose something happens in your application where something is supposed to be updated on the page (e.g., someone types something in). Instead of just automatically updating the DOM, what React will do is create a completely new virtual DOM and compare this new virtual DOM with the old virtual DOM, comparing everything in both virtual DOMs. When it comes across any differences in the new DOM compared to the old DOM, instead of updating the entire DOM tree (which is very common and incredibly expensive computationally), React will only update the thing(s) that changed. The next time something happens that is supposed to update the page will result in React again making a new virtual DOM, comparing it to the old virtual DOM, and updating the actual DOM with any differences that occurred, and so on and so forth:

So the speed is not only in that you are changing/updating only what needs to be changed/updated but also that you are comparing regular JavaScript objects with regular JavaScript objects instead of DOM elements with DOM elements. That is what makes all of this worth it and what makes React so fast.

Components

So far we have rendered things two ways via ReactDOM.render. First, we put our JSX directly in the render:

...
<script type="text/babel">
ReactDOM.render(
<h1>Sanity Check</h1>,
document.getElementById('root')
)
</script>
...

Second, we assigned our JSX to a variable and then passed that variable to render:

...
<script type="text/babel">

const title = 'React From the Beginning';
const name = 'Robert Bunch';

function saleOn() {
return true;
}

const markup = <div className="row">
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src="https://picsum.photos/400/400/" />
</div>
<div className="card-content">
<p>{title}</p>
<p>{name}</p>
</div>
<div className="card-action">
<a href="#">${saleOn() ? 9.99 : 59.99}</a>
</div>
</div>
</div>
</div>

ReactDOM.render(
markup,
document.getElementById('root')
)
</script>
...

As fate would have it, neither of these ways is the preferred React way. Of course these ways work, but React is meant to be component-based. Everything in React is meant to be a component. It's just a bunch of little Lego pieces or modules pieced together to actually formulate your UI.

How will this actually work? We will create a Card component by creating a Card.js file with the following as its contents:

// Card.js
function Card() {
const title = 'React From the Beginning';
const name = 'Robert Bunch';
function saleOn() {
return true;
}

return (
<div className="row">
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src="https://picsum.photos/400/400/" />
</div>
<div className="card-content">
<p>{title}</p>
<p>{name}</p>
</div>
<div className="card-action">
<a href="#">${saleOn() ? 9.99 : 59.99}</a>
</div>
</div>
</div>
</div>
)
}

It is worth noting here that everything that happens above the return in the Card function is just plain JavaScript. That is, structurally, we will have the following for components:

function Card(props) {
// a bunch of pure JavaScript

return (
// a bunch of JSX to be processed by Babel
)
}

Now we can create our index.html to use this Card component in the following manner:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First Component</title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="./Card.js" type="text/babel"></script>
</head>

<body>

<div id="root"></div>

<script type="text/babel">

ReactDOM.render(
<Card />,
document.getElementById('root')
)
</script>

</body>

</html>

Two things to note right out of the gate:

  1. type="text/babel": This will not work: <script src="./Card.js"></script>. It is imperative that this script have an attribute of type="text/babel" so Babel will know to process it. So we need <script src="./Card.js" type="text/babel"></script>, as indicated above.
  2. <Card />: This is essentially going to invoke the Card function in Card.js and will get the return value, which is a bunch of JSX, from the Card function living in Card.js. If you use a different name for your component such as <Different />, then this will not work. We need <ComponentName /> to match the function ComponentName in whatever file the ComponentName function is located in. Often a file's name will reflect what component lives within it, but we could just as well have named Card.js as Honk.js and the function inside Craziness and then we would need to have <script src="./Honk.js" type="text/babel"></script> and use <Craziness />. The point is that <ComponentName /> needs to match the function name returning a component in whatever file that component lives in. As stated previously, your file name will generally be named to reflect what component lives within it and that component will be used as such within your application.

Recapping, our script tag must have type="text/babel" because what the script tag points to (e.g., the Card function) needs to be processed by Babel. That way, when the Card function is invoked within our code by <Card />, what we are getting is processed JSX by Babel instead of just a bunch of gibberish that JavaScript won't understand. Hence, something like

ReactDOM.render(
<Card />,
document.getElementById('root')
)

will actually make sense because <Card /> is effectively being replaced by what the Card function returns from Card.js (a bunch of JSX that, thanks to type="text/babel", has been processed by Babel into JavaScript the browser will understand).

Of course, at this point, we're not so much improving how anything looks, but we are getting closer and closer to how React is meant or intended to function. Now, <Card /> is a component, and this component looks like an HTML tag, but it always starts with an uppercase letter. The reason for that is because when React is parsing through our code, if it runs into lowercase stuff, it's going to assume it's either an HTML tag or it's an XML tag. If, however, it seens an uppercase letter, it will assume it's actually a component. As the docs note: "Always start component names with a capital letter. React treats components starting with lowercase letters as DOM tags. For example, <div /> represents an HTML div tag, but <Welcome /> represents a component and requires Welcome to be in scope. To learn more about the reasoning behind this convention, please read JSX In Depth."

Components are not just the backbone of React but really React in its entirety. As we get to do cooler and cooler things with React, we'll quickly start to notice that the entire UI or entire front-end is really just a whole bunch of components. We're always going to start with one (probably something like <App />) that has a whole bunch of components in it where the components inside have components in them, etc. It really is like a bunch of Legos that fit together to make up something awesome.

Props

Anytime you have a component in React you have the option of adding attributes to that component. Before we just had <Card /> but we could also have something like <Card name="Daniel Farlow" job="Developer"/>. Of course, attributes in HTML are typically things like id, class, width, etc., but now we are making up our own "attributes". What will happen to the attributes we put on our component is that when the component is called (i.e., think previously about how <Card /> invoked or called the Card function in Card.js that returned a bunch of processed JSX), the component is handed an argument which is always called props (since it's a local variable you can call it whatever you want, but convention is to call it props so you should always do that). So in our Card.js file our Card function really should have function Card(props) { ... }.

Suppose our Card.js file looked like this:

function Card(props) {
console.log('The props: ', props);

const title = 'React From the Beginning';
const name = 'Robert Bunch';

function saleOn() {
return true;
}

return (
<div className="row">
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src="https://picsum.photos/400/400/" />
</div>
<div className="card-content">
<p>{title}</p>
<p>{name}</p>
</div>
<div className="card-action">
<a href="#">${saleOn() ? 9.99 : 59.99}</a>
</div>
</div>
</div>
</div>
)
}

And our index.html file was this:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First Component</title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="./Card.js" type="text/babel"></script>
</head>

<body>

<div id="root"></div>

<script type="text/babel">

ReactDOM.render(
<Card name="Daniel Farlow" job="Developer"/>,
document.getElementById('root')
)
</script>

</body>

</html>

Then upon firing up everything and looking in the console, we would see the following:

Note that props is just an object and that every attribute that you give your component is sent over as props. The props naming convention is sensible and conventional because props is actually a JavaScript object that will have a property for every attribute that you set on your component. The attribute name itself will be a key on the props object while what you assign to the named attribute will be the key's value. So something like <Card name="Daniel Farlow" job="Developer"/> results in props looking like { name: 'Daniel Farlow', job: 'Developer' }. The upshot of all of this is that you can use props within your components to make the components dynamic and reusable.

For example, if our HTML looks like this:

...
<script type="text/babel">
ReactDOM.render(
<Card title="React From the Beginning" name="Robert Bunch"/>,
document.getElementById('root')
)
</script>
...

Then our Card.js file can look like this:

function Card(props) {

const { title: courseTitle, name: courseInstructor } = props;

function saleOn() {
return true;
}

return (
<div className="row">
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src="https://picsum.photos/400/400/" />
</div>
<div className="card-content">
<p>{courseTitle}</p>
<p>{courseInstructor}</p>
</div>
<div className="card-action">
<a href="#">${saleOn() ? 9.99 : 59.99}</a>
</div>
</div>
</div>
</div>
)
}

How awesome is that! What does this accomplish? Essentially, it lets us create a single Card component that could be used numerous times in different contexts:

  <script type="text/babel">
ReactDOM.render(
<React.Fragment>
<Card title="React From the Beginning" name="Robert Bunch"/>
<Card title="Apache Kafka Series" name="Stephane Maarek"/>
</React.Fragment>,
document.getElementById('root')
)
</script>

Two things to note here:

  1. React.Fragment: See the docs for more on fragments. The basic idea is that fragments let you group a list of children without adding extra nodes to the DOM. There is also a shorter way to use them with <> and </>, but note that this does not support the use of keys or attributes. You can see more about keyed fragments and why that might be a good idea (think of creating a description list).
  2. We just created two Card components very easily by only passing what was different to the cards. That is, we want the cards to look the same but obviously have only the content that's relevant or specific to them.

This is the power of components! The fact that we can send data down to them makes it almost like a function where instead of passing arguments we are passing props. One thing to note is that props are immutable. They are managed by the parent and never managed by the component itself (of course, you could change this behavior by hijacking things with JavaScript within your component, but that's a big no-no and defeats the whole point of components). All components with their props are meant to be pure; that is, given the same props, the component should always look the same. So you never manually mutate or change props. They're meant to be pure.

We can even use the data object we have in data.js:

const data = [
{
course: "React From the Beginning",
instructor: "Robert Bunch"
},
{
course: "Apache Kafka Series",
instructor: "Stephane Maarek"
},
{
course: "Music Production in Logic Pro X",
instructor: "Tomas George"
},
{
course: "Unity Game Development",
instructor: "Jonathan Weinberger"
}
]

And we can use this via <script src="./data.js"></script> in our index.html file to make several cards dynamically:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First Component</title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="./Card.js" type="text/babel"></script>
<script src="./data.js"></script>
</head>

<body>

<div id="root"></div>

<script type="text/babel">
ReactDOM.render(
<div className="row">
<Card title={data[0].course} name={data[0].instructor}/>
<Card title={data[1].course} name={data[1].instructor}/>
<Card title={data[2].course} name={data[2].instructor}/>
<Card title={data[3].course} name={data[3].instructor}/>
</div>,
document.getElementById('root')
)
</script>

</body>

</html>

Of course, we will get to the point soon where we can easily loop through everything and create it even more dynamically.

Multiple components in an array (and unique "key" prop warning)

One thing that is super cool in React is that you can build your components in an iterative fashion. Consider the following code:

let cards = data.map(courseData => (
<Card data={courseData}/>
))

console.log(cards)

ReactDOM.render(
<div className="row">
{cards}
</div>,
document.getElementById('root')
)

If we do something like this, then currently we will get a warning like the following:

What this means is that cards is an array of React elements, the type is a function, and it has a key with a value of null (also has ref with value of null, etc.). The problem is that you can basically think of this as a linked list in the context of the virtual DOM. React wants these elements in the array to have keys so that if the state of the application changes React knows which thing changes so that it doesn't have to update the entire thing. If you don't provide a key, then it won't know what it actually needs to change and will have to rebuild the whole array which is expensive which is against the whole ethos of React. The gist: Whenever you build an array of React elements, if you give a key, then React will be a lot faster. When using map to build an array, you could just make each key the value of the index (this practice is a last resort though--the key should ideally be something unique to each item like an ID). Hence, our code becomes:

let cards = data.map((courseData, index) => (
<Card data={courseData} key={index}/>
))

console.log(cards)

ReactDOM.render(
<div className="row">
{cards}
</div>,
document.getElementById('root')
)

And the warning goes away.

Components as classes

Up until now the only type of component we have made is a regular JavaScript function. For example:

function Card(props) { ... }

This is a great and common way to make functions that are simply presentational or stateless. It means they don't need to make any decisions. But there is another very important way to make components. And this way is with classes. (The introduction of Hooks has made it possible to use stateful components with functions, and we'll get to all of that much later.)

The way we have kind of done things before is like the following:

class Card {
return (
<h1>Sanity Check</h1>
)
}

But this is not okay anymore because inside of a class in JavaScript the only thing you are allowed to define are properties and methods. We can't just run JavaScript code. We need to put the JSX that we want to return inside a method in order to properly adhere to how classes work in JavaScript. What method should we use? It turns out there is a convention/mandate from React and the docs spell this out in more detail: "The only method you must define in a React.Component subclass is called render(). All the other methods are optional." Seems important! If you use a class then, you must have a render method:

class Card {
render() {
return (
<h1>Sanity Check</h1>
)
}
}

It turns out the code above is still not good enough. It's not enough to just define the component using a class. In order to get all the goodness of a React component, we actually need too extend the React component (i.e., the Card class needs to first inherit everything from the React.Component class and then extend it by using our own methods, properties, and the like):

class Card extends React.Component {
render() {
return (
<h1>Sanity Check</h1>
)
}
}

What this does is it makes our class, Card, a subclass of React.Component. So React.Component has a bunch of stuff we are going to "inherit". All the cool stuff that belongs with being a React component can now be used as part of our Card component. A couple things to note here. This

function Card(props) {
return (
<h1>Sanity Check</h1>
)
}

is exactly the same as

class Card extends React.Component {
render() {
return (
<h1>Sanity Check</h1>
)
}
}

right now. They're functionally the same although syntactically different. A class gives you all sorts of power that you do not have with a function. (At least this used to be the case--this is different now with the advent of Hooks.) From the docs: "React lets you define components as classes or functions. Components defined as classes currently provide more features which are described in detail on this page. To define a React component class, you need to extend React.Component." So more often than not you will be making components as classes instead of functions. Again, if you make a class component, then it must have a render method; otherwise, the component is totally useless. It's the only method that a class must have in React. So don't forget to add it! And we have to use extends React.Component for class components; otherwise, the class is just a regular garden-variety class. And sometimes you'll make classes that you don't extend (e.g., utility classes that don't have anything to do with React just to clean up your JavaScript).

If you've done much with classes before in JavaScript, then you will know that the constructor is another method available to classes (every class in JavaScript gets this method whether or not the class in question is a React component or just a garden-variety class). The constructor method will run when an instance of the class (or in our case a React component instance) is created. This gives us the ability to initialize instance variables and initialize state (the notion of state in React is a very important one we will get to momentarily).

Per the docs on constructor(props): "If you don't initialize state and you don't bind methods, you don't need to implement a constructor for your React component. The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) [within constructor(props)] before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs. Typically, in React constructors are only used for two purposes: Initializing local state by assigning an object to this.state and binding event handler methods to an instance."

The upshot of all of this is that, in order for us to use state, we need to call the super(props) method within our constructor:

class Card extends React.Component {
constructor(props) {
super(props);
// more stuff to come
}

render() {
return (
<h1>Sanity Check</h1>
)
}
}

The constructor will run every time a new Card is created. And every time the constructor is called the first thing that should get called is super, which is the constructor method of the parent class. So every time we create a new Card our own constructor will run, but we will first call super in order to run the constructor of the parent/super class (i.e., React.Component). As the docs noted, the constructor is really only necessary if we are trying to initialize state and/or trying to bind methods to a class instance.

One last thing to note right off the bat is how props are accessed within classes. For a regular JavaScript class, how do you refer to properties of the class? With the this keyword:

class Dog {
constructor(name, friends, legs) {
this.name = name;
this.friends = friends;
this.legs = 4;
}

sayName() {
return this.name;
}
}

In the silly example above, how did we refer to the name property of the class instance within the sayName method? Not by name but by this.name. Why? Because, as MDN notes (under the "Instance properties") subsection, instance properties must be defined inside of class methods (they give the following example with the simple Rectangle class):

class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}

The name instance property for Dog is defined inside of the constructor class method. For example, something like let myDog = new Dog('Archie', ['Felix', 'Bruno']); will result in the myDog instance of Dog having instance properties of

  • name: 'Archie'
  • friends: ['Felix', 'Bruno']
  • legs: 4

And we access these instance properties in methods within the class using the this keyword. So something like myDog.sayName() results in the sayName method of the Dog class being called on the Dog instance of myDog. Hence, this points to myDog in this case so myDog.sayName() would result in 'Archie'.

In the exact same manner (albeit more complicated fashion), in a React class component, we do not refer to props within the render method simply as props but by this.props. Where do our class instance properties get defined? When we create the component in question and pass props to them; for example, if PlayingCard were a class component, then something like <PlayingCard value="12" suit="Spades" /> would result in implicitly having this.props.value = "12" and this.props.suit = "Spades" underneath the hood:

class PlayingCard extends React.Component {
constructor(props) {
super(props);
console.log(props); // { value: "12", suit: "Spades" }
}

render() {
const cardValueMap = {
'1': 'ace',
'2': 'two',
'3': 'three',
'4': 'four',
'5': 'five',
'6': 'six',
'7': 'seven',
'8': 'eight',
'9': 'nine',
'10': 'ten',
'11': 'jack',
'12': 'queen',
'13': 'king'
}

const { suit, value } = this.props;
const translatedValue = cardValueMap[value];
const phrase = translatedValue.slice(0,1).toUpperCase()
+ translatedValue.slice(1)
+ ' of ' + suit;

return(
<p>This card is a {phrase}</p>
)
}
}

If we throw <script type="text/babel" src="PlayingCard.js"></script> into our index.html and <PlayingCard value="12" suit="Spades"/> into our app.js, we will get This card is a Queen of Spades placed in the DOM.

Returning to our original Card example, the once functional component

function Card(props) {
console.log(props)
const { course: courseTitle, instructor: courseInstructor, image: courseImage } = props.data;

return (
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src={courseImage} />
</div>
<div className="card-content">
<p>{courseTitle}</p>
<p>{courseInstructor}</p>
</div>
<div className="card-action">
<a href="#">$9.99</a>
</div>
</div>
</div>
)
}

can become the functionally equivalent (albeit imbued with many more potential powers now) class component:

class Card extends React.Component {
constructor(props) {
super(props);
}

render() {
const { course: courseTitle, instructor: courseInstructor, image: courseImage } = this.props.data;

return (
<div className="col s2">
<div className="card hoverable small">
<div className="card-image">
<img src={courseImage} />
</div>
<div className="card-content">
<p>{courseTitle}</p>
<p>{courseInstructor}</p>
</div>
<div className="card-action">
<a href="#">$9.99</a>
</div>
</div>
</div>
)
}
}

Worth noting is that for functional components we could run any plain JavaScript we wanted before we returned the JSX we wanted to. Similarly, in a class component, within the render method, we can run any plain JavaScript we want before we return the JSX we want, as indicated above and also in the PlayingCard example.

Using classes is actually rather helpful in general when thinking about your React components because it almost enforces the concept of encapsulation on the developer. That is, a given object should not only contain all of its own data but should also contain all the methods that change and effect that data. So we are going to try to make our components as self-sufficient as possible. They'll carry their data around with them, and they'll also carry their render around with them. They'll carry other methods around with them as well so that ideally you'll be able to move these components across applications or parts of applications and it should be as seamless as possible.

React Essentials: Recap

What Is React and Why Do We Need It?

React is a bunch of JavaScript that someone else wrote that makes it easier to do front-end web development. React modernizes front-end web development by doing the following:

  • Making the front-end modular via components (components are encapsulated, meaning they manage themselves)
  • Making it much easier to maintain across teams and even years
  • Simplifying state changes in an application
  • Getting front-end applications to run very, very fast
  • Separating front-end from back-end

React in Its Simplest Form

Recall our first React program:

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>First React Program</title>
<!-- This is React proper -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<!-- This is ReactDOM -->
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<!-- This is Babel -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
</head>

<body>

<div id="root"></div>

<script type="text/babel">
ReactDOM.render(
<h1>Sanity Check</h1>, // <-- This is JSX
document.getElementById('root')
)
</script>

</body>

</html>

Here's the breakdown from the comments above:

  • React: Everything we make is a "React" element, not a DOM element.
  • ReactDOM: ReactDOM uses its render method to take our React elements and inject them into the actual DOM (on the webpage).
  • JSX: Allows us to commingle HTML and JavaScript. This saves us from having to write TONS of JavaScript.
  • Babel: Converts our JSX into something the browser can read.

More explicitly, React allows us to create React elements via React.createElement. ReactDOM allows us to use ReactDOM's render method to get React elements into the actual DOM from the React's virtual DOM (i.e., onto the actual webpage). JSX, via Babel, makes it possible for us to write

<h1 className='root'>Sanity Check</h1>

instead of writing

React.createElement("h1", { className: "root" }, "Sanity Check");

as you can see through an interactive session on babeljs.io. We are also allowed to embed JavaScript expressions in JSX using the so-called wax-on/wax-off technique: { <-- wax-on | expression here | wax-off --> }:

const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(
element,
document.getElementById('root')
);

ReactDOM and the virtual DOM

React keeps track of all React elements in a "virutal DOM" object. Whenever something changes, React builds a new "virtual DOM" object and ReactDOM compares them:

Because React elements are just plain JavaScript objects, React is very, very fast with its ability to update only what is necessary in the real DOM (where objects are far more weighted and computationally expensive to create); that is, ReactDOM updates only the part of the DOM that needs to change.

Component Basics

  • Components are the backbone of React.
  • They are little pieces that make up the entire UI.
  • They always start with a capital letter.
  • They must close along with all other React elements (i.e., neither <br> nor <Card something="else"> will do; instead, we must use <br /> and <Card something="else" />, as an example).
  • Components look like HTML tags in JSX (but uppercase): <Card />.
  • Components always return some HTML so ReactDOM has something to put on the page. Remember that React is a front-end UI so every component we make has to have something for the UI in it. So every component has to return some HTML (technically JSX is what gets returned, which is processed by Babel and subsequently React uses what was processed to create React elements which subsequently get created as actual DOM elements via ReactDOM).
  • Components can be pure functions (stateless or simple).
  • Components can be classes (stateful or complex).
  • Note: Hooks make it possible to use stateful functions but we'll get to that later.

Prop Basics

  • Components are a lot like JavaScript functions.
  • They can be rendered as many times as needed.
  • In order to change when they render, components can be sent any data you wish (like an argument in a function). The data that gets passed to a component is called props.
  • A prop is anything inside a Component call after the Component name and looks like an HTML attribute:

  • A prop's value comes after the =, just like an HTML attribute.
  • A prop value can be accessed inside the component. props is always an object.
  • The props object will have a property for each prop that was passed when the component was created.
  • The value of the property will be the value of that prop:

Components in an Array

  • React allows us to put components in an array.
  • JSX can unpack that array.
  • We typically use .map() to build the array of components.
  • map() builds a new array and expects a return value.

Components as Classes

  • Aside from regular JavaScript functions, components can also be made as classes.
  • Classes themselves do not return JSX; they have a render method that returns JSX.
  • Classes always extend React.Component (unless you have a utility class you are using that has nothing to do with React) so that your custom class, which via extends is a subclass of React's Component superclass, inherits all the goodness that comes from being a React component. When initializing state or binding methods, constructor(props) {super(props); ... } will need to be used at the top of your class.
  • Props work the same way in a class as they do in a function except we refer to the props in a class by this.props instead of simply props.
  • Classes (currently) come with more powers than plain JavaScript functions, as noted in the docs.

Breaking Down Components into Smaller Parts (i.e., subcomponents)

  • Components can contain other components.
  • Think of it like the DOM:
    • A div often lives inside another div.
    • A <City /> can live inside a <CitiesContainer />.

JavaScript Inside Components

Recall that you can embed JavaScript expressions in JSX by putting any valid JavaScript expression (simply: any valid unit of code that resolves to a value) inside curly braces in JSX: { JavaScriptExpression }. Importantly, we cannot use non-expression JavaScript code inside of JSX. So a question becomes: Where can you perform "normal" or "heavy duty" JavaScript within components before returning your desired JSX? This depends on whether or not you have a function or a class:

  • Function: In functional components, regular JavaScript can be used before you return your JSX:
function FunctionComponent(props) {

// Do all non-expression or "heavy-lifting"
// JavaScript stuff here before returning JSX below

return (
// JSX
)
}
  • Class: In class components, regular JavaScript can be used before you return your JSX in the render method (as with regular JavaScript classes, plain JavaScript cannot be left outside of a method, whether it be the constructor method or a custom method):
class ClassComponent extends React.Component {
constructor(props) {
super(props);
// initialize state; bind methods
}

// JavaScript CANNOT go here

render() {

// Do all non-expression or "heavy-lifting"
// JavaScript stuff here before returning JSX below

return(
// JSX
)
}
}

State and Events (and create-react-app)

npx vs. npm in regards to create-react-app (use npx!)

This answer on Stack Overflow does an excellent job of making clear what npm and npx actually are and why we might want to use one over the other. Some key takeaways:

  • npm: Manages packages but doesn't make life easy executing any.

  • npx: A tool for executing Node packages.

  • npx comes bundled with npm version 5.2+.

  • The major advantage of npx is the ability to execute a package which wasn't previously installed:

    $ npx create-react-app my-app

    The above example will generate a react app boilerplate within the path the command had run in, and ensures that you always use the latest version of a generator or build tool without having to upgrade each time you're about to use it.

Hence, for what we are about to learn, instead of doing something like

$ sudo npm install create-react-app -g

you will want to do

$ npx create-react-app my-app

every time you want to make a new react application using create-react-app. Using npx in this fashion ensures you are getting the latest version of create-react-app instead of installing create-react-app globally and subsequently possibly using an outdated version.

Modernizing our web development with create-react-app

Up until now, we have used actual .html files and actual .js files and we connected the JavaScript to our HTML by means of script tags. Nothing is wrong with this, and it's probably rather important to start learning React this way; otherwise, you risk it being very mystical how React is actually working under the hood. But this is not how modern React development is done. Instead, Facebook has made (and maintains) create-react-app which is a node module available through NPM. And this thing does a crazy good job of kicking out everything you could possibly need to build out a great React application from scratch. Building production applications before this came out was highly frustrating (due to the amount of configuration that was needed in so many respects).

We are now going to fire off a React application from a scaffold using create-react-app. And if you've used, say, Rails before, Rails creates a scaffold for you when you do rails g, Laravel is similar with php artisan, the Express generator is similar with express-generator, and so on. Most frameworks and platforms have something like this where it will just create a whole bunch of files for you (i.e., a "scaffolding"). It's kind of like saying, "Developers need this to make almost anything so we'll just give you what you probably need and you can take it from there."

We will run

$ npx create-react-app first-cra

where first-cra simply stands for "First Create-React-App." This process will probably take a decent bit of time because a ton of dependencies are being installed. We can then cd first-cra and run npm start as we are informed in the terminal and we'll get a boilerplate React application launched on localhost:3000 (or we will be prompted for another port on which to listen if that port is already in use). Now, if you look at the node_modules folder, you will see hundreds of node modules. The webpack node module is what creates the development server for us. We also see a whole bunch of jest node modules. And jest is a unit-testing framework for JavaScript, and React has been kind enough to install everything to get that up and running if we choose to do testing (as we should!). We also see a whole bunch of eslint node modules. And those are meant to make error messages when we run our code to be much friendlier and much more helpful.

Now let's inspect our package.json for a moment:

{
"name": "first-cra",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

Recall that the package.json file gives metadata about our application; that is, it tells the world about our application. And one of the most important things the package.json file does is it determines your dependencies, meaning if someone downloads your app and wants to use it, you have to have the node modules listed in the dependencies:

"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1"
}

As for why npm start worked to launch our development server, we can see this from the scripts part in our package.json:

"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}

And we can actually check these scripts out for ourselves if we want:

node_modules -> react-scripts -> scripts -> build.js | eject.js | init.js | start.js | test.js

So the scripts folder in the react-scripts node module is where all these little scripts live. When we type npm start, npm is going to go check out our package.json file and see if there is a start command. There is! So it's going to call the react-scripts node module and it's going to grab the start script that lives in start.js. Basically, this is where Babel, Webpack, and basically all of the development stuff is going to get started. And you can check out build.js for the build script as well. And in react-scripts we see a package.json file which details that node module's dependencies, several of which have their own dependencies, and so forth (that is how we end up with a huge folder of node modules at the root of our application).

Apart from the testing modules, React needs react, react-dom, and react-scripts, the last of which actually creates our entire development environment for us.

Understanding the file structure given to us by create-react-app

As of this writing (April 26, 2020), this is the file structure you will get when running create-react-app on cra-app-name:

cra-app-name
┣ node_modules
┣ public
┃ ┣ favicon.ico
┃ ┣ index.html
┃ ┣ logo192.png
┃ ┣ logo512.png
┃ ┣ manifest.json
┃ ┗ robots.txt
┣ src
┃ ┣ App.css
┃ ┣ App.js
┃ ┣ App.test.js
┃ ┣ index.css
┃ ┣ index.js
┃ ┣ logo.svg
┃ ┣ serviceWorker.js
┃ ┗ setupTests.js
┣ .gitignore
┣ README.md
┣ package-lock.json
┗ package.json

We are going to spend almost all of our time in the src folder. The entry point for the entire application is index.js inside of src:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();

This is where everything is going to start. And some of it may look a little funky, but at least one bit should look very familiar (read more about React.StrictMode on Medium or directly from the docs; the gist is that StrictMode is a tool for highlighting potential problems in the application and, like Fragment, StrictMode does not render any visible UI--it simply activates additional checks and warnings for descendants):

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

Above, we are rendering a component call App (in StrictMode) and we are putting in whatever HTML element has an id of root. But where is React actually getting root at? It is getting it from index.html which appears in the public folder. Note that anything placed in the public folder will be publicly accessible. All of its contents will be statically served--it won't be part of your React application (unless it's a link or something like that). This is where you would typically place files like images, audio clips, videos, JavaScript that is not part of React (like a library or your own utility files), CSS files, etc. The point, however, is that index.html is located in this public folder and we see <div id="root"></div> contained therein so that is where everything is going from ReactDOM.render in our index.js file in the src folder.

At the top of our index.js file we see the following:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

These are all import statements and this is an ES6+ feature which is not supported by any browser (at least as of this writing). import makes it very easy to modularize your stuff. The point is that import is kind of like doing

import React from 'react';

instead of

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>

as we have been doing previously. Using import React from 'react'; indicates we are fetching the React object from the react node module. So the react node module had better exist! The same thing applies to ReactDOM coming from react-dom. We brought these in before by adding React and ReactDOM to the window object by our script tags, but now they're being added by Node.js through Webpack. We can also import our index.css easily with one line. And we can import App from ./App exactly as we just described instead of doing something like

<script src="./App.js"></script>

as before. Well, what actually lives in App.js? Let's see:

import React from 'react';
import logo from './logo.svg';
import './App.css';

function App() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}

export default App;

import a new component into App.js before you make it so you can benefit from the linter

When making a new component, it is often wise to go ahead and import that component in your App.js so that any issues you encounter while making your component are caught by the linter and you can be warned about them.

By default, if you are not importing your component in App.js, then the linter will not be able to catch any problems in your component since your component is not actually being loaded into or looked at in App.js.

What is state?

Previously, we talked about components and about props. Components form the bedrock of React and props is how we pass data to components. The third main staple or backbone element of React is the concept of "state." As discussed previously, React came onto the web development scene with a couple of goals in mind, the first being to modularize web development because modern web development has massive front-ends that are getting totally out of control. The codebases are huge and have to be modularized. Components solve that. We need to speed up the front-end in the browser, and the virtual DOM solves that by minimizing the things that actually need to change. The other really big thing that Facebook wanted to do when they made React was create some kind of state management tool.

State is a fairly universal concept in programming. An oversimplified definition might be that state is the value of a variable or variables at any given time. And the whole issue of state management is really a problem in modern web development. An example to illustrate this might be by imagining your daily vist to Amazon's website. We can describe the process in different states:

  • State 1: What happens when you order a product on their site? What are the different steps of the process? Maybe you start by having an empty cart. You've never been there before and maybe we say that you have a pristine empty cart.
  • State 2: You place an item in your cart. Suppose you add a super cool book about gardening to your cart, but then soon after decide you don't want the book so you remove the book from your cart. Does that take you back to State 1 where you had a pristine empty cart? No. It does not. It does not take you back to State 1. Why not? Because, in Amazon's eyes, you now have a dirty empty cart.
  • State 3: By dirty empty cart what is meant is that something used to be in the cart and is no longer there. The difference between a pristine empty cart and a dirty empty cart is very important to Amazon. Why? Because they will want to market to you as you cruise around the site (e.g., "Hey, remember that gardening book you were thinking about buying? You should buy that! Or maybe these related books."). They are going to want to keep track of what you put in and take out of your cart.
  • State 4: Maybe this time you add a book to your cart on woodworking because gardening just wasn't doing it to you. Are we now in the same situation we were in during State 2? No, of course not. The books are different.
  • Other States: You can imagine other states being things such as maybe you go to the checkout, the payment section, the delivery phase, the customer service phase, etc. And maybe at some point you need to return something. The idea is that there's any number of things that can happen during this whole process, and Amazon whats to track this carefully.

If you think about a simple web form, the same thing will be true. Maybe you have a super simple form with a username entry, a password enty, and a submit button. As soon as you type something into the username or password fields, something has just happened. The DOM has just changed, however slightly. The value of an input box in the DOM has just changed. Every time we have changed something in the DOM we have mutated the DOM. For a tiny form, the changes might be totally nominal. Maybe we have two password fields, the second to verify the first and vice-versa. Another thing we might want to know is how many times this particular form has been changed: does it seem like human behavior or robot behavior? Have they submitted the form several times? And so on. So there's lots of options and there's lots of reasons to be interested in how the page is actually changing.

Another example would be something like playing Tic-Tac-Toe in JavaScript (in fact, one of the main tutorials on first learning React uses this as an example of highlighting all of the key concepts in React). This would also have state. What is the state of the game right now? What place is occupied by which player? Whose turn is it? And so on.

The bottom line is that there needs to be some state management tool so that we can figure out where certain things are in a process because the application needs to make decisions about what the current state is, and we want to avoid making mutations (i.e., updating the DOM) when they're not necessary.

Instead of manipulating the DOM directly, we will allow components to manage their themselves, and we can pass that around as needed to other components. We will use functional programming to update the state which means we won't ever change state directly--we will let React know that something happens that way React can go through all of its paces and all the right channels to make sure that whoever needs to know or whatever needs to happen can happen in the appropriate order.

A note about React.StrictMode double-invoking functions (constructor, render, etc.)

Since

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

is part of the new boilerplate code added to index.js in the src folder when scaffolding out a project using create-react-app, it's good to know that some mildy funky or unexpected behavior can result from using React.StrictMode. Specifically, if we make a dummy component like

import React, { Component } from 'react';

class StateInAction extends Component {
constructor(props) {
super(props);
console.log('Constructor is running')
}
render() {
return (
<h1>State in Action</h1>
);
}
}

export default StateInAction;

and drop this in App.js like so:

import React from 'react';
import './App.css';
import StateInAction from './components/StateInAction/StateInAction.component';

class App extends React.Component {
render() {
return (
<div className="App">
<StateInAction />
</div>
);
}
}

export default App;

Then we will actually see Constructor is running logged to the console twice. And if we change our index.js to only have

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

as used to be the case when creating projects with create-react-app, then Constructor is running will only be logged once. What accounts for this behavior?

This answer on Stack Overflow succinctly answers this question and points us to the docs for more information. Essentially (from the SO post), in recent versions of React, rendering uses strict mode when running in development. Strict mode intentionally double-calls the constructor and render functions to better detect unexpected side effects. From the docs: Strict mode can't automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

Running in production build at least in one use case did not result in the same double render of the class component.

Getting started with state (where to initialize, do's and don'ts, etc.) and events (binding in constructor, etc.)

State (initializing and setting/updating)

You will want to initialize state in the constructor method of your class component underneath super:

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

The this in our case is the class instance itself because we are inside of the constructor. And state is an instance variable or it's like a variable for this particular object. State is very special. You can make anything you want inside of your constructor just like you can in any other language. But the state variable is very very special and unique to React. We can start by defining a property for our state object:

this.state = {
text: 'State in Action!'
}

And we can use it like so:

import React, { Component } from 'react';

class StateInAction extends Component {
constructor(props) {
super(props);
this.state = {
text: 'State in Action!',
};
}

render() {
const { text } = this.state;

return <h1>{text}</h1>;
}
}

export default StateInAction;

Just to illustrate how to update state for the moment, even though we will probably never do this in the future (a whole new world will open up once we start exploring events), we can set up a setTimeout in our constructor:

constructor(props) {
super(props);
this.state = {
text: 'State in Action!',
};

setTimeout(() => {
this.setState({
text: 'State Changed!'
})
}, 2000)
}

This method will run one time (when the component is created). We'll run super which will get us everything great about being a React component, this.state will set up our local state variable, and setTimeout will get called. Note how we don't try to manually change state by doing something like this.state.text = 'State Changed'!. We never mutate state manually by ourselves. As the docs note in the context of certain things we should know about setState:

  • Do not modify state directly. [Read more.]
  • State updates may be asynchronous. That is, React may batch mulitple setState calls into a single update for performance--because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state. To account for this, you should use a second form of setState that accepts a function rather than an object, and that function will receive the previous state as the first argument and the props at the time the update is applied as the second argument. See the docs for more, this Medium post, this Stack Overflow thread, and the bottom of this article by Tyler McGinnis where he notes that, "It's rarely used (i.e., the second form of setState where you pass a function) and not well known, but you can also pass a function to setState that receives the previous state and props and returns a new state, just as we're doing above. And not only is nothing wrong with it, but it's also actively recommended if you're setting state based on the previous state." So if you are setting state based on previous state (e.g., counters and the like or a whole host of other things), then passing a function to setState instead of just an object is what you will want to do. [Read more.]
  • State updates are merged. [Read more.]

As alluded to above, even though something like this.state.text = 'State changed' looks fine with regular old JavaScript, this is something we never do in React (i.e., never set state manually via an assignment like the above). The only time you should be doing this.state = ... is inside of the class constructor when the component is created for the very first time. The reason for that (some of which is mentioned above in the three different points) is because React needs to do a whole bunch of stuff under the hood when the state changes. So instead of changing the state ourselves, we can hand it to React and React can run its own method (i.e., setState), do a whole bunch of stuff, and ultimately change it for us. Why all this rigmarole? In object-oriented programming, something like setState is called a setter (it is the counter-companion to a getter; read more about setters and getters on MDN). A setter is a function whose job is to mutate something else (i.e., set something and implement how that something should be set without exposing the innerworkings of how everything is set to the user or developer; in this context, we want to set the state in some fashion, and React wants to handle how the state is set by exposing its setState method on the React.Component prototype (remember our class inherits all the goodies from the Component class since our class extends React.Component)). So setState is basically saying, "Hey, if you want to change a state variable, then you tell me, and I will change it for you. Don't change it yourself."

Events (getting started)

Events in React in General

As the docs detail in regards to handling events (something we will address in more detail in the next note), handling events with React elements is "very similar" to handling events on DOM elements. There are two key syntactical differences:

  • React events are named using camelCase, rather than lowercase.
  • With JSX you pass a function as the event handler, rather than a string.

Hence, instead of something like

<button onclick="activateLasers()">
Activate Lasers
</button>

as would normally be the case, in React, we would have something like

<button onClick={activateLasers}>
Activate Lasers
</button>

The docs also note that you cannot return false to prevent default behavior in React. Instead, you have to call preventDefault explicitly. They give an example in plain HTML of preventing the default link behavior of opening a new page:

<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>

In React, we would have something like the following instead:

function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}

return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}

What we care about here, however, is not just preventing default behavior (which is no doubt nice to know) but how events are treated in general in React (the docs note that the e referenced above is a SyntheticEvent, something we will address in just a moment). Remember that React elements are not DOM elements until they are made so by a need to update the DOM. In particular, the JSX we use is transpiled by Babel into JavaScript the browser can understand in order to turn our newly created React elements into freshly minted DOM elements. So how do event listeners work in React? Typically, without React, you would use the addEventListener Web API for Event Targets to add a listener to a DOM element after it was created. Consider this silly example (adding a button to the top of body in the DOM which, when clicked, alerts hello!):

let newElement = document.createElement('button');
let buttonText = newElement.innerText;
newElement.innerText = 'Click me to hear me say hello!'
newElement.addEventListener('click', (e) => {
console.log(e); // MouseEvent { ..., type: "click", ... }
alert('hello!');
})
document.body.prepend(newElement);

Here we have a "normal" event e that is a MouseEvent of type click. In the React example, they noted that e is a synthetic event? Well what the heck is a synthetic event? Their docs give more information, but here are some observations:

If we click on the Mouse Events link, then we will see the React onClick event which effectively maps to placing addEventListener on an element and listening for an event of type click. Functionality wise, the code

let newElement = document.createElement('button');
let buttonText = newElement.innerText;
newElement.innerText = 'Click me to hear me say hello!'
newElement.addEventListener('click', (e) => {
console.log(e); // MouseEvent { ..., type: "click", ... }
alert('hello!');
})
document.body.prepend(newElement);

in plain JavaScript and

<button onClick={(e) => {console.log(e); alert('hello!')}}>Click me to hear me say hello!</button>

in JSX when using React are the same. In fact, when we log e to the console in the React example when the button is clicked we see Class { ..., nativeEvent: MouseEvent, type: "click", ... }.

SO WHAT IS THE POINT OF ALL THIS TEDIUM? The basic point is this: React may not handle events exactly how you are used to or how you expect. In particular, when you define a component using an ES6 class, a common pattern in React is for an event handler to be a method on the class (since for JSX we pass functions as event handlers rather than strings). Where this pattern can get a little tricky and confusing, however, is understanding the meaning of this in reference to the functions we pass as event handlers to React elements (i.e., the functions we use as JSX callbacks).

Possibilities for Confusion: When Event Handlers are Class Methods (the usual)

Consider the following example from the docs that illustrates where confusion may arise when using a class method as an event handler (as the docs also note, this is a very common pattern in React development so we need to make sure we understand what is going on):

class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}

What's happening here? Why do we need the this.handleClick = this.handleClick.bind(this); line in the constructor (try commenting that line out and clicking the button; the application will break and you'll get a nasty error implicitly communicating that this is undefined: TypeError: Cannot read property 'setState' of undefined)? As the docs note, "Class methods in JavaScript are not bound by default. So if you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called." But why? What would this normally (i.e., outside of React) refer to in regards to DOM event handlers? As the MDN docs note about this (towards the bottom there are two subsections: this "As a DOM event handler" and this "In an inline event handler"): "When a function is used as an event handler, its this is set to the element on which the listener is placed (some browsers do not follow this convention for listeners added dynamically with methods other than addEventListener()). [...] When the code is called from an inline on-event handler, its this is set to the DOM element on which the listener is placed."

Here is the key question of concern if we are to have any hope of understanding why it is necessary (depending on our approach) to bind event handler class methods in the constructor in React: How is this set in regards to event handlers for React elements? To answer this question, we have to dive under the hood a bit, but the result is worth it. What follows is largely a paraphrased version of this excellent answer on Stack Overflow.

React Event Handlers: Understanding the Meaning of this

We can better understand how React treats event handlers by considering and pondering a few key points in succession:

  • this is dynamic
  • how React handles event handlers
  • why binding works
  • why arrow function properties work
  • recap: 4 examples
  • main takeaway
this Is Dynamic

To better understand the React-specific situation, a brief introduction to how this works is in order; in particular, we will want to observe how this can lose meaning (i.e., become undefined) when the method referring to this gets passed around.

One of the keys to understanding this in JavaScript lies in knowing that this is a runtime binding and depends on the current execution context, hence why this is commonly refered to as "context". Sometimes (not just in React) we have to specify the context in which we want this to be considered (i.e., give information on what we want to be the current execution context when this is referenced) in order to get desired/expected results. Binding is necessary on occasion because you sometimes you lose "context" (this is especially true when dealing with React event handlers, as we will soon see).

To clearly illustrate how this problem (i.e., losing "context") can arise, consider the following basic snippet:

const myDog = {
name: 'Archie',
sayName: function() {
return this.name;
}
}
console.log(myDog.sayName()); // 'Archie' ... all good so far!

In this example, we get 'Archie', as expected. But consider this example:

const sayMyDogName = myDog.sayName;
console.log(sayMyDogName()); // undefined ... Uh oh! Why!?!?

It may be unexpected to find that we get undefined logged and thrown back in our faces--where did 'Archie' go? The answer lies in "context" or how you execute a function. Compare how we call the functions:

// Example 1
myDog.sayName();
// Example 2
const sayMyDogName = myDog.sayName;
sayMyDogName();

Notice the difference. In the first example, we are specifying exactly where the sayName method is located (we'll use "method" to refer to a function that is supposed to be bound to an object, and "function" for those not): on the myDog object:

myDog.sayName();
^^^^^

But in the second example, we store the method into a new variable, and then we use that variable to call the method without explicitly state where the method actually exists, thus losing context:

sayMyDogName(); // which object is this function coming from?

And therein lies the problem. When you store a method in a variable, the original information about where that method is located (i.e., the "context" in which the method is being executed) is lost. Without this information, at runtime, there is no way for the JavaScript interpreter to bind the correct this. Without specific context, this does not work as expected (in the second snippet, undefined is logged instead of 'Archie' because this defaults to the global execution context (window when not in strict mode, or else undefined) when it cannot be determined via specific context; and in our example window.name does exist thus yielding undefined).

To fix this issue in our own example, we can use Function.prototype.bind() method: "The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called." So instead of

const sayMyDogName = myDog.sayName;
console.log(sayMyDogName()); // undefined ... Uh oh! Why!?!?

we can create a new function (i.e., sayMyDogName) that, when called, has its this keyword set to myDog:

const sayMyDogName = myDog.sayName.bind(myDog);
console.log(sayMyDogName()); // 'Archie' ... yay! Who's a good boy!?
How React Handles Event Handlers

Here is an example of a React component suffering from the dreaded error resulting from the this problem: TypeError: Cannot read property 'setState' of undefined:

import React from 'react';

class CountExperiment extends React.Component {
constructor(props) {
super(props);
this.state = {
clicks: 0,
};
// this.handleClick = this.handleClick.bind(this); // <-- needed to work properly; why?
}

handleClick() {
this.setState((prevState, props) => ({
clicks: prevState.clicks + 1,
}));
}

render() {
return <button onClick={this.handleClick}>{this.state.clicks}</button>;
}
}

export default CountExperiment;

But why and how does the previous subsection about "this is Dynamic" relate to the this issue we have here? Because the component above and "Example 2" considered previously both suffer from an abstraction of the same problem. Specifically, note how React handles event handlers:

// Edited to fit this description; React performs other checks internally
// props is the current React component's props,
// registrationName is the name of the event handle prop (e.g., "onClick")
let listener = props[registrationName];
// Later, listener is called

So when you put onClick={this.handleClick} in your JSX in React, the this.handleClick method is eventually assigned to the variable listener (if you go down the rabbit hole of how events in the event queue are executed, invokeGuardedCallback is called on the listener). But now you can hopefully see why we have the problem we do: since React has assigned this.handleClick to listener, we are no longer specifying exactly where handleClick is coming from! From React's point of view, listener is just some function, not attached to any object (the CountExperiment React component instance in this case). This is similar to what happened in our much simpler previous example:

const myDog = {
name: 'Archie',
sayName: function() {
return this.name;
}
}

const sayMyDogName = myDog.sayName;

console.log(sayMyDogName()); // undefined

From JavaScript's point of view, sayMyDogName is just some function, not attached to any object (the myDog object in this case).

The takeway: In both situations (sayMyDogName with myDog and handleClick with CountExperiment), we have lost context and thus the interpreter cannot infer a this value to use inside the sayName method in the myDog object and the handleClick method in the CountExperiment object (i.e, React component instance).

Why binding Works

From everything we have discussed, it is sensible to wonder: If the interpreter decides the this value at runtime, then why can I bind the handler so that it does work? The reason is because you can use Function#bind to guarantee the this value at runtime (specificially, we want to guarantee that the the this for our event handler points to the class instance or new React component). This is done by setting an internal this binding property on a function, allowing it to not infer this (we want to set this *explicitly):

this.handleClick = this.handleClick.bind(this);

When the line above is executed, presumably in the class constructor, the current this is captured (i.e., the React component instance) and set as an internal this binding of an entirely new function, returned from Function#bind. The bind method ensures the interpreter will not try to infer anything about the value of this when this is being calculated at runtime but explicitly use the provided this you have given it.

To see this in action in a semi-illustrated fashion, consider the following component:

import React from 'react';

class IllustratedBinding extends React.Component {
constructor(props) {
super(props);
this.state = {
unboundedClicks: 0,
boundedClicks: 0
}

this.handleClickBound = this.handleClickBound.bind(this);
// |____|
// ^----- React component instance
}

handleClickUnbound() {
this.setState((prevState, props) => ({
unboundedClicks: prevState.unboundedClicks + 1
}));
}

handleClickBound() {
this.setState((prevState, props) => ({
boundedClicks: prevState.boundedClicks + 1
}));
}

render() {
return (
<React.Fragment>
{/* v----------------- not an event handler (so never assigned as "listener")
|_________________| */}
<p onLoad={console.log(this)}></p>

<button onClick={this.handleClickUnbound}>handleClickUnbound</button>
{/* |_______________________|
^-------------- this.handleClickUnbound eventually assigned as
"listener", thus losing context */}

<button onClick={this.handleClickBound}>handleClickUnbound</button>
{/* |_____________________|
^--------------- this.handleClick eventually assigned as "listener",
but context not lost since context was explicitly
bound to the current "this" captured in the constructor
method (i.e., the React component instance) */}
</React.Fragment>
)
}
}

export default IllustratedBinding;

Although all the details (and comments) are included in the code above, it's worth noting the following:

  • <p onLoad={console.log(this)}></p> is simply included in order to show that referring to this within a class results in what is expected, namely IllustratedBinding { ... }. It's when you try to use this in event handlers that we have to be mindful of how our event handler class method will eventually get assigned as "listener" in the future.
  • handleClickUnbound: As its name implies, the handleClickUnbound method has not been bounded. Hence, when the button is clicked on which this method is passed as the callback, we will get the dreaded this error: TypeError: Cannot read property 'setState' of undefined. The reason is because this.handleClick is eventually assigned to listener, thus losing its context (i.e., the React component instance).
  • handleClickBound: As its name implies, the handleClickBound method has been bounded. Specifically, what is the current this in the class constructor (or anywhere else in the class for that matter)? The this in the class constructor refers to the React component instance we are creating. Hence, we capture the current this in the constructor via bind: By declaring this.handleClickBound = this.handleClickBound.bind(this); in the constructor, we ensure whenever this.handleClickBound is called that the this being referred to within handleClick points to the React component instance we are creating.

A slightly more cleaned up "visual" example of the above is given below as an image:

Why Arrow Function Properties Work

Arrow function class properties currently work through Babel based on, as an example,

handleClick = () => { /* Can use this just fine here */ }

being effectively transpiled into

constructor() {
super();
this.handleClick = () => {}
}

And this works due to the fact that arrow functions do not bind their own this but take the this of their enclosing scope, namely the constructor's this in this case which points to the React component instance, thus giving us the correct this.

It's actually a lot more complicated. React internally tries to use Function#bind on listeners for its own use, but this does not work with arrow functions as they simply do not bind this. That means when this inside the arrow function is actually evaluated, the this is resolved up each lexical environment of each execution context of the current code of the module. The execution context which finally resolves to have a this binding is the constructor, which has a this pointing to the current React component instance, allowing it to work.

Recap: 4 Examples

Now that we have a much better understanding of how event handlers work in React and why it's necessary to bind methods in the constructor (unless you bind elsewhere which is atypical and not recommended or you use arrow functions which frequently occurs and is recommended), we can consider 4 approaches or examples of how to go about defining event handlers in React. The following component is meant to illustrate these examples:

import React from 'react';

class FourExamples extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOnWithoutBind: true,
isToggleOnWithBind: true,
isToggleOnWithArrow: true,
isToggleOnWithArrowCallback: true,
};

// For explicit binding in Example 2, as we learned was needed
this.handleClickWithBind = this.handleClickWithBind.bind(this);
}

handleClickWithoutBind() {
this.setState((prevState, props) => ({
isToggleOnWithoutBind: !prevState.isToggleOnWithoutBind
}));
}

handleClickWithBind() {
this.setState((prevState, props) => ({
isToggleOnWithBind: !prevState.isToggleOnWithBind
}))
}

handleClickWithArrow = () => {
this.setState((prevState, props) => ({
isToggleOnWithArrow: !prevState.isToggleOnWithArrow
}));
};

handleClickWithArrowCallback() {
this.setState((prevState, props) => ({
isToggleOnWithArrowCallback: !prevState.isToggleOnWithArrowCallback
}));
}

render() {
const buttonMargin = {
display: 'block',
marginTop: '5px',
marginBottom: '20px',
};
const divStyle = {
textAlign: 'left',
marginLeft: '10px',
fontFamily: 'monospace',
fontSize: '20px',
};

return (
<div style={divStyle}>

{/* Example 1 (incorrect!!!) */}
handleClickWithoutBind:
<button style={buttonMargin} onClick={this.handleClickWithoutBind}>
{this.state.isToggleOnWithoutBind ? 'ON' : 'OFF'}
</button>

{/* Example 2 (use explicit binding: correct) */}
handleClickWithBind:
<button style={buttonMargin} onClick={this.handleClickWithBind}>
{this.state.isToggleOnWithBind ? 'ON' : 'OFF'}
</button>

{/* Example 3 (exploit fact that arrow functions do not bind their own "this": correct) */}
handleClickWithArrow:
<button style={buttonMargin} onClick={this.handleClickWithArrow}>
{this.state.isToggleOnWithArrow ? 'ON' : 'OFF'}
</button>

{/* Example 4 (works but not performant since different callback is created for each render) */}
handleClickWithArrowCallback:
<button style={buttonMargin} onClick={() => this.handleClickWithArrowCallback()}>
{this.state.isToggleOnWithArrowCallback ? 'ON' : 'OFF'}
</button>
</div>
);
}
}

export default FourExamples;

And you can see the results below:

Other class methods defined in this manner should be similarly bound in the constructor. See this article for more about how bind works as well as this Tyler McGinnis article for understanding this, especially in the context of the call, apply, and bind functions.

Main Takeaway

In most cases, you should either bind your class methods in the constructor:

class SampleClass {
constructor(props) {
super(props);
this.state = { ... };

this.oneMethod = this.oneMethod.bind(this);
this.twoMethod = this.twoMethod.bind(this);
this.threeMethod = this.threeMethod.bind(this);
...
}

oneMethod() { ... };
twoMethod() { ... };
threeMethod() { ... };
...

render() {
return (
// JSX
)
}
}

Or you should simply use public class fields syntax with arrow functions:

class SampleClass {

oneMethod = () => { ... };
twoMethod = () => { ... };
threeMethod = () => { ... };
...

render() {
return (
// JSX
)
}
}

Events in React

Now that we have a sense of what state is and the basics of how it works, the question becomes: Why would we ever want to use it? When is state really useful? We're going to answer that now. The short answer lies in the fact that React is JavaScript, and it's a UI that runs in the browser, and JavaScript is an event-based language. What causes something to happen on any webpage? What causes the UI or DOM to change? It's usually because the user clicked on something, submitted a form, changed an input box, etc. So we are going to talk about how events work now because state and events are very closely related. Of course, state can change at any point inside of an application or a component, but it's most commonly going to change with events.

If we create a simple button, then historically we might do something like the following to add an event listener to it:

import React, {Component, Fragment} from 'react';

class SimpleEvents extends Component {
constructor(props) {
super(props);
this.state = {
msg: 'stateActivated'
}

}
render() {
document.querySelector('.btn').addEventListener('click', () =>{
console.log('Button was clicked!')
})
return (
<Fragment>
<button onclick="myFunction()">Click me!</button>
</Fragment>
);
}
}

export default SimpleEvents;

This is a very native JavaScript way of doing this. But what's the problem with what we've done above? We'll get an error: TypeError: Cannot read property 'addEventListener' of null. We are getting this error because, in React, our button actually hasn't been added to the DOM yet. So we can't do addEventListener because, well, the first time render runs there's nothing to add an event listener on (the JSX hasn't been returned yet). So we can't use addEventListener right now in this way (we can later if we want due to to how the component lifecycle works in React).

The other way you would normally do something like the above is you would add the event listener directly to the element we wanted to be listening on:

<button onclick="myFunction()">Click me!</button>      

This is very similar to how we do things in React (with a couple of caveats). In React, virtually every DOM event is still available, but we use camelCase, and we hand the event the callback we want to run:

<button onClick={myFunction}">Click me!</button>      

Unlike in native JavaScript, we don't invoke functions as part of the event listener but instead pass functions, as seen above. We pass a callback in our JSX that is going to get run later. The reason for this is pretty simple: In native JavaScript we invoke the code we want to run later (as in onclick="myFunction()", for example), but in React we don't invoke code we pass code. For something like

<button onClick={console.log('Test')}>Click me!</button>      

what we pass to onClick is going to get evaluated, and we don't return anything in the example immediately below (technically, a function returns undefined in JavaScript when nothing else is explicitly returned). So what's actually sent back is undefined as a result of console.log('Test'). Hence, on the first render, we get Test logged to the console, but every time thereafter we essentially have onClick=undefined. So nothing will really happen when we click on the button because we're technically not telling React to do anything when we click it. The overall point is that we do not run code in onClick or other listeners for events; instead, we pass code. We pass a callback. And we can do this in two ways.

Instead of running our code as above, we can simply create a new anonymous function that runs the code for us:

<button onClick={() => console.log('Test')}>Click me!</button>      

Using the code above will result in us logging Test to the console every time the button is clicked, as desired. This kind of pattern is fairly common in React. Again, we are not running console.log('Test')--we are passing a function that runs console.log('Test'). In general, this kind of pattern, albeit common, is also a bit messy. If possible, it's often wise to define a class method, which we can then pass by name:

import React, {Component, Fragment} from 'react';

class SimpleEvents extends Component {
constructor(props) {
super(props);
this.state = {
msg: 'stateActivated'
}
}

handleClick() {
console.log('Test');
return console.log('Real Test')
}

render() {
return (
<Fragment>
<button onClick={this.handleClick()}>Click me!</button>
</Fragment>
);
}
}

export default SimpleEvents;

This is functionally the same as what we had before. Again, we are not going to run this.handleClick but pass this.handleClick. What we can't do is this.handleClick() because this will just result in Test being logged to the console once when the inital render occurs, but this.handleClick() returns undefined. What if we tried to do something kind of silly like

// ...
handleClick() {
console.log('Test');
return console.log('Real Test')
}
//...
<button onClick={this.handleClick()}>Click me!</button>
// ...

Would this work? What would happen? Well, instead of returning undefined now, we would return console.log('Real Test') which itself returns undefined; hence, we would just end up with Test and Real Test being logged to the console in succession. The "fix" (only for the sake of illustration ... you would never want such a silly function!):

// ...
handleClick() {
console.log('Test');
return () => console.log('Real Test')
}
//...
<button onClick={this.handleClick()}>Click me!</button>
// ...

Then we would get Test logged on the first render and Real Test logged every time we clicked the button thereafter.

In summary, those are the two ways:

  1. We can define a method and pass that method.
  2. We can define an anonymous function within the React element itself that runs our code.

Now let's consider a different kind of element, an input. You don't really click on input other than to change it. So with an input we will use onChange:

<input type="text" placeholder="Enter some text!" onChange={this.handleChange} />

And if we want to, we can have a simple

handleChange() {
console.log('User changed the input')
}

And as a user types this handler will fire every single time the user presses a key because any time something changes inside of the input box we are logging User changed the input to the console. We can have more fun by using something like

handleChange(e) {
console.log(e.target.value)
}

which will actually what's currently in the input box.

Let's now consider a form to round things out. You don't click on or change forms--you submit them. So we will use onSubmit. If we wrap our previous elements in the form then something interesting will happen:

handleSubmit() {
console.log('Form submitted!')
}

render() {
return (
<Fragment>
<form onSubmit={this.handleSubmit}>
<button onClick={this.handleClick}>Click me!</button>
<input
type="text"
placeholder="Enter some text!"
onChange={this.handleChange}
/>
</form>
</Fragment>
);
}

If we hit Enter while inside of the input box, then you will very briefly see Form submitted! logged to the console. Why does this message disappear? What's happening is common to JavaScript (in other languages there are mechanisms to prevent this), namely we have to manually tell the browser to not send the form forward to the next page because we don't have one. Normally your form tag might have an action or a method on it (e.g., action='/process_review_submission' with method='post'). But for our case right now, we want to stop the form from being sent forward. A very important topic when it comes to handling events is that every event in React some with an event object that will automatically be passed (even if we don't use it as we haven't so far except in the example of using e.target.value, where e is referring to the event). The event that is passed in each case comes with a preventDefault method which ... prevents the default behavior of what the event normally does, which, in the case of a form submission, is to send the form forward to the next page. We are going to prevent that default behavior by using e.preventDefault within our handleSubmit handler.

Available events

If we head over to SyntheticEvent in the docs, we can see what all events are available in React, and they use the SyntheticEvent wrapper (essentially a class) in order to try and normalize the way every browser works with events, just meaning that, for example, Firefox may have a slightly different event object than Chrome does, than Safari does, and so on. Instead, React uses the SyntheticEvent which makes sure that they pretty much all behave the same way regardless of the browser.

If you go down to Supported Events, you will see that they have categorized them:

And we can take a glance at what event names there are for a given category; for example, we get the following for Mouse Events:

  • onClick
  • onContextMenu
  • onDoubleClick
  • onDrag
  • onDragEnd
  • onDragEnter
  • onDragExit
  • onDragLeave
  • onDragOver
  • onDragStart
  • onDrop
  • onMouseDown
  • onMouseEnter
  • onMouseLeave
  • onMouseMove
  • onMouseOut
  • onMouseOver
  • onMouseUp

There are the Pointer Events for when you actually move your cursor around, and so on. So the Synthetic Events page can be a great reference to refer to if you are wondering how a particular event works.

Changing state with an event

We are now going to tie the two together (i.e., state and events and how we can let events change state and thereby the UI). The best way to see how state and events can be tied together is by considering a basic example involving them (see if you can tell what is intended inside of the component):

import React, { Component, Fragment } from 'react';

class EventAndState extends Component {
constructor(props) {
super(props);
this.state = {
titleText: '',
inputText: '',
};
}

handleClick = (e) => {
this.setState({
titleText: 'Try some more possibilities!',
inputText: e.target.value,
});
};

handleChange = (e) => {
this.setState({
titleText: e.target.value,
inputText: e.target.value,
});
};

handleSubmit = (e) => {
this.setState({
titleText: 'React is so awesome!',
inputText: '',
});
e.preventDefault();
};

render() {
const { inputText, titleText: dynamicTitleText } = this.state;

return (
<Fragment>
<h1>
{dynamicTitleText === ''
? 'Start typing for a dynamic title!'
: dynamicTitleText}
</h1>
<form onSubmit={this.handleSubmit}>
<button onClick={this.handleClick} type="button">
Click me to try more dynamic titles!
</button>
<input
value={inputText}
type="text"
placeholder="Enter some text to change title!"
onChange={this.handleChange}
/>
</form>
</Fragment>
);
}
}

export default EventAndState;

A few things to note from the above example component:

  • Any change in state will cause the EventAndState component to render itself again (unless we explicitly tell it not to, as we will see is a possibility when looking at the component lifecycle). Since the state changes in this component on every single keystroke in the input field, it renders however frequently the user types (as well as when the button is clicked or the form submitted). More to the point: Every time our component rerenders, React will compare the old virtual DOM with the new virtual DOM; in this case, if a keystroke has been entered, then there will be a difference between the old virtual DOM and the new virtual DOM, thus resulting in a change being reflected in the actual DOM.
  • Worth repeating: Any time state changes in a React component, render will be called again. Every time. Unless we force it to not render.
  • In this example, for our button, we need to specify type='button'. Why? Because, as this post observes, buttons like <button>Click to do something</button> are submit buttons. We can set type='button' on the button in order to change this.
  • In the handleSubmit method, we must call e.preventDefault() in order to prevent the form from submitting and the browser trying to move the page to something else that doesn't exist (basically resulting in a worthless refreshing of the page since we don't specify where the browser should go upon form submission).

This is awesome! We are beginning to see the power of state. We have it all in one place. We essentially have one source of truth for the component. We can update the UI very very efficiently using the same events we are used to but simply allow React to change the variables and then manage the rerender process down below.

Side note: Eventually, you will find that you want to handle onChange for several inputs, and you'll notice you're basically rewriting the same code over and over, a giveaway that you could possibly do something more efficient. Without going into much detail here, this brief post on Medium discusses easy data binding with a generic onChange hander:

onChange = e => this.setState({ [e.target.name]: e.target.value });

See the post for more details, but this can be pretty handy on a number of occasions. Worth noting is that this is certainly not a catch-all solution as it currently stands: it won't work if, say, you need to validate the format of an email address, check the strength of a password, etc. In those cases, you'll clearly need to run some conditional logic inside of setState to handle those edge cases appropriately.

Practicing with managing state with events

Here were the original assignment instructions:

Assignment instructions

Make a new Component called StatePractice
  • import react and Component
  • make it a class, specifically a subclass of Component
  • export the class as default
  • add the constructor and super
  • initialize state in the constructor with a property of message and a value of empty string
  • add a render method
  • return a sanity check
Add StatePractice to app.js
  • go to app.js and import the component (remove EventAndState)
  • render the component in app.js
Add event and state change to StatePractice
  • remove sanity check and replace it with an input box and an h3
  • in the h3, render the message piece of state
  • use onFocus on the input box so that when the user clicks on the input, state updates
  • setState on "message" to notify the user that they agree to the site terms of service by filling out the form
Add another event
  • add the onMouseEnter event to the h3 so that it clears the text when the user hovers over it
Add another tag and event
  • add an image tag (point at any URL)
  • add another property to your state variable in the constructor called imageWidth and init to an empty string
  • use the onLoad event to grab the image width
  • if the image width is greater than 100px, then console.log("Your image is big!")

Extension

We can extend on this basic assignment to create the following behavior (disregard the top and bottom bars blinking and changing colors--that's due to the console being open during the demo):

If you try to mimic the behavior above, there are a few things worth noting:

  • The textarea field should dynamically have the same width as the image.
  • You should actually alert the user upon first and only first focus of the text area.
  • As the docs note, the syntax for setState is setState(stateChange[, callback]). We know a fair amount about the stateChange required argument which can be a regular object (which is good to use when new state does not depend on previous state) or a callback (that automatically receives previous state and current props as available arguments). But setState also accepts an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally, the React recommends using componentDidUpdate() (a lifecycle method we will get to before long) for such logic instead.
  • As the docs note about refs (in the component code below a single ref is used in order to send focus back to the textarea once a user has hovered over the title): Refs provide a way to access DOM nodes or React elements created in the render method: "In the typical React dataflow, props are the only way that parent components interact with their children. To modify a child, you re-render it with new props. However, there are a few cases where you need to imperatively modify a child outside of the typical dataflow. The child to be modified could be an instance of a React component, or it could be a DOM element. For both of these cases, React provides an escape hatch." They go on to recommend different use cases for refs: Managing focus (as is done in the component below), text selection, or media playback; triggering imperative animations; integrating with third-party DOM libraries.
  • There's also some in-line styling. We'll get to more styling possibilities later.
import React, { Component } from 'react';

class StatePractice extends Component {
constructor(props) {
super(props);
this.state = {
message: 'You agree to our terms of service by filling out the form.',
dynamicTitle: '',
inputText: '',
imageWidth: '',
termsAcknowledged: false,
};
this.textInput = React.createRef();
}

handleMouseEnter = (e) => {
this.setState(
{
dynamicTitle: 'New dynamic title inbound!',
inputText: '',
},
() => {
this.textInput.current.focus();
}
);
};

handleFocus = (e) => {
if (!this.state.termsAcknowledged) {
alert(this.state.message);
}
this.setState((prevState, props) => ({
termsAcknowledged:
prevState.termsAcknowledged || !prevState.termsAcknowledged,
}));
};

handleChange = (e) => {
this.setState({
dynamicTitle: e.target.value,
inputText: e.target.value,
});
};

handleImgLoad = (e) => {
this.setState(
{
imageWidth: e.target.width,
},
() =>
this.state.imageWidth > 100
? console.log(`Your image is big! (${this.state.imageWidth}px)`)
: null
);
};

handleFormSubmit = (e) => {
e.preventDefault();
this.setState({
dynamicTitle: 'Thanks for submitting! Try a new one!',
inputText: '',
});
};

render() {
const { inputText, dynamicTitle, imageWidth } = this.state;

return (
<form action="" onSubmit={this.handleFormSubmit}>
<h3 onMouseEnter={this.handleMouseEnter}>
{dynamicTitle === ''
? 'Type a message to begin! (or hover over this title at any time to begin a new one)'
: dynamicTitle}
</h3>
<button style={{ cursor: 'pointer' }}>Click to submit!</button>
<br />
<textarea
style={
imageWidth === ''