Skip to main content

Learning React

published on

I've been working on a new project lately, and I figured it was time to bite the bullet and finally look into React, the hot new front-end Javascript framework that everyone in the web developer community is talking about. If you're not familiar with the web developer community, it truly is very fad driven. I heard about React months ago, but didn't bother learning it because I figured that in a month or two, everyone would have moved on to some other popular JS framework. However, React seems to have some staying power, and since Lektor's admin pages are built with it, I decided to finally learn it.

The upshot is, React is very nice! It introduces a few innovative ideas for Javascript development, but mostly builds on a solid framework of functional programming and familiar ideas. I was able to get up and running pretty quickly, and while the tooling was a challenge to set up, the end result works quite well. The code is clear and understandable, and I can see how everything fits together. I think I'll be using React a lot more in my future projects.

JSX

The first hurdle to learning React is understanding JSX. JSX is a dialect of Javascript that allows you to write HTML directly into your code. Without JSX, if you wanted to create and return a complex HTML element, you might do so using jQuery, like this:

function makeInput(contents) {
  return $("<input>", {type: "text", value: contents, "class": "form-control"})
}

Incidentally, a lot of people don't know that you can use jQuery to create structed HTML elements. It's quite a nice API. However, it's not as nice as what JSX allows you to do:

function makeInput(contents) {
  return <input type="text" value={contents} className="form-control" />
}

You might look at that snippet and instantly think, "Wait a minute, that's not valid JavaScript! That won't work at all!" Well, you're right, it's not valid JavaScript. However, it is valid JSX, and it gets compiled into valid JavaScript that does the same sort of thing that the first snippet does. The general term for this idea is syntactic sugar -- it makes a certain idea clearer or easier to express in a programming language, without fundamentally changing how the language works.

So why is JSX important for React? Because React makes you define your own web components in a language that is similiar to HTML, and allows you to mix and match your custom components with HTML. A classic example is embedding an interactive map into your website. Before React, you had to do something like this, as shown in the offical Google Maps documentation:

<div class="map-wrapper">
  <p>Here's a map!</p>
  <div id="map"></div>
  <script>
    var map;
    function initMap() {
      map = new google.maps.Map(document.getElementById('map'), {
        center: {lat: -34.397, lng: 150.644},
        zoom: 8
      });
    }
  </script>
</div>

That's not bad, but what if we could just do this, instead?

<div className="map-wrapper">
  <p>Here's a map!</p>
  <GoogleMap
    defaultCenter={{ lat: -34.397, lng: 150.644 }}
    defaultZoom={8}
  />
</div>

<GoogleMap> is a custom React component that will handle all the details of inserting an interactive Google Map element into your page and setting it up properly. JSX allows you to ignore the implementation and just use it, putting the principle of encapsulation to work. And this isn't a contrived example: react-google-maps is a popular project on GitHub, with a live demo if you're curious.

The only annoyance I've found with JSX so far is that you can't use the class parameter, because "class" is a reserved keyword in JavaScript, and JSX is built on JavaScript. Instead, you use className, which is part of JavaScript's Element API. To be honest, I don't understand why JSX can't use "class" as syntactic sugar for "className", so that the developer doesn't have to think about it. On the scale of things, it's a very minor quibble.

Components

Once you have a basic understanding of JSX, you can start using React. React makes you think about your user interface as a group of "components": logical pieces of your UI that fit together into larger pieces. There is an excellent blog post about how to do this, but I'll reproduce the relevant parts here. Let's say you're creating an online store, and you need to build a filterable table of products, which will look something like this:

"A table of items and prices. There is a search bar at the top, to filter the contents of the table."

This is a component of a user interface: a logical unit that you could place on a page. However, this component is itself made up of other components, as you can see here:

"The same image as before, with a box around the search bar, a box around the table, and a box around each row in the table."

By dividing up each component into simpler and simpler pieces, it becomes easier to reason about it. Now, instead of implementing the whole filterable table component at once, we can represent it like this:

  • FilterableProductTable (orange)
    • SearchBar (blue)
    • ProductTable (green)
      • ProductCategoryRow (turquoise)
      • ProductRow (red)

Each of these components is smaller, and therefore easier to implement. We can also make them work together, by passing data between parent components and children components. For example, when a user types into the SearchBar component, it tells the parent FilterableProductTable component what the user typed. Then, the FilterableProductTable component tells the ProductTable component what data to display, and it only passes data that matches what the user has typed. The ProductTable component doesn't have to know or care about how the filtering works -- it just renders whatever data it receives!

ES6 and Babel

Onto the code! Here's an example of a basic "Hello, World!" component in React:

import React from 'react';

class HelloWorld extends React.Component {
  render() {
    return <p>Hello, World!</p>
  }
}

Wait a minute, this doesn't even look like JavaScript! Imports? Classes? What's going on here?!

The answer is, this is JavaScript, but it's not the JavaScript you're used to. JavaScript is an implementation of a standard called ECMAScript, and the ECMAScript standard continues to evolve. The JavaScript that we know today is based on ECMAScript version 5, and unfortunately, it is an ugly language, full of gotchas and syntax warts. (I love JavaScript, but it has a lot of problems.)

However, the Ecma standards body has dramatically changed the language with version 6, and it's actually quite nice. As a result, many people writing React are writing it using ECMAScript version 6, also known as ES6 or ES2015 (because that version was formalized in the year 2015). Since web browsers don't fully support ES6 yet, this code is compiled back into ECMAScript 5 (the JavaScript that we all know and love deal with) before it's sent to the browser.

How does that compilation happen? In a word, Babel. Babel is a pluggable JavaScript compiler that has two very popular preset plugin bundles: es2015, for compiling ES6 into ES5, and react, for compiling JSX. So, feed your beautiful, future-oriented ES6 JSX files into Babel, and it will spit out the ugly-but-practical ES5 JavaScript files that today's browsers can use.

As I said, the tooling is a challenge for React -- but the end result is that developers get to write clearer, better code, which I'm all in favor of. Personally, I think the trade-off is worth it. That being said, you can write React without using ES6 or JSX, if you really don't want to set up Babel. But once you've got your tooling set up correctly, writing ES6 JSX is a joy, and it's surprisingly intuitive.

React

Finally, we can start talking about React itself! React isn't an MVC framework per se, it's more of a structure for composable elements that communicate. There are no templates, unless you count JSX as a template (which it sort of is). You write your components, wire them together, and let React handle state changes easily and automatically.

What does that actually mean, though? Well, here's an example:

class GreetingBox extends React.Component {
  constructor() {
    super();
    // Set up the state of this component. By default, "name" is empty.
    this.state = {name: ""};
    // Bind the setName() function to this class instance.
    this.setName = this.setName.bind(this);
  }
  setName(value) {
    // Call the setState() function, which is built-in to React.
    this.setState({name: value});
  }
  render() {
    // Return our two sub-components, wrapped in a <div>.
    // This component is the only component that manages state, so we have to
    // inform the children components of the current state of the GreetingBox
    // -- and the NameInput component needs a way to update the GreetingBox's
    // state.
    return <div>
      <NameInput name={this.state.name} setName={this.setName} />
      <Greeting name={this.state.name} />
    </div>
  }
}

class NameInput extends React.Component {
  constructor() {
    super();
    // Bind the setName() function to this class instance.
    this.handleChange = this.handleChange.bind(this);
  }
  handleChange(event) {
    // Call the setName method that GreetingBox passed to this component,
    // with the value from the <input> HTML element.
    this.props.setName(event.target.value);
  }
  render() {
    // Setting the onChange attribute allows React to bind the handleChange
    // function on this class instance whenever it needs to.
    return <label>
      What's your name?
      <input type="text" value={this.props.name} onChange={this.handleChange} />
    </label>
  }
}

class Greeting extends React.Component {
  render() {
    let name = this.props.name;
    if (name.length == 0) {
      return <p>I don't know who you are.</p>
    } else {
      return <p>Hello, {name}!</p>
    }
  }
}

When we first run this code, we'll end up with the following HTML:

<div>
  <label>
    What's your name?
    <input type="text" value="">
  </label>
  <p>I don't know who you are.</p>
</div>

When you type in the name field, the greeting will automatically update to reflect whatever you've written. For example:

<div>
  <label>
    What's your name?
    <input type="text" value="David">
  </label>
  <p>Hello, David!</p>
</div>

You can even try to type in HTML tags to break it, and React will automatically escape them for you. It's pretty nifty!

There are a few important things to note about the way that React works, which I think are pretty cool.

HTML is Lowercase, Components are Capitalized

React components have names that begin with a capital letter, which distinguishes them from HTML tags that are lowercase. This allows you to mix HTML and components while still easily keeping track of which is which.

One-Directional State

State always flows downward. This helps you to untangle messy, state-driven UIs: state must always be kept in the highest-level component that encompasses all other components that use that state. That's why the state in this example is kept in the GreetingBox component: both sub-elements need to access that state information. Parents can extend tools to their children that allow the child to update the parent's state, such as the setName method in the example, but the child doesn't actually need to maintain any state itself.

Automatic Re-Rendering

Any time the state in a component is modified, that component is re-rendered automatically, and all of its children are re-rendered as well if necessary. This is what makes the UI feel snappy and alive: it always displays the latest information about the state of the world.

What Next?

The next thing I need to look into is Redux, which is a state container for JavaScript applications. It is often paired with React applications, although it may be overkill for my use-case.

And just what is that use-case, you ask? What am I building? That's a bit of a secret right now, but hopefully I'll be able to reveal it soon. I'll give you a hint, though -- it's related to Lektor!