The Web Platform has gone a long way since HTML5 was first made popular and people started looking into JavaScript as a language that could do build complex apps. Many APIs have emerged and there is an abundance of content out there about how the browser can leverage all this.

This specific article series will go a step further and focus on how these powerful technologies can be leveraged in practise, not to building cool demos and prototypes, but how real practitioners use them in the trenches. In this (post)HTML5 series, we'll go beyond buzzwords and get practical insight from experts about what has actually worked for them. We'll also talk about technologies (like AngularJS) that go a step further, and define the future of how the standards and web development will evolve.

This InfoQ article is part of the series “Next Generation HTML5 and JavaScript”. You can subscribe to receive notifications via RSS.

Related Sponsored Content

In August 2014, Codecademy decided to adopt Facebook's library for writing JavaScript UIs, React.js, as part of an overhaul of our learning environment. Initially, we ran into issues finding examples of how React could apply to an application as complex as ours, given that most tutorials focus on the specifics of toy-sized demo apps as opposed to more general discussions of the problems specific to larger applications. In this article, I will focus on both an overview of React and on highlighting some specific considerations around using React in larger web applications.

What is React?

In short: React is a library for building user interfaces with JavaScript. Instead of the usual approach to writing user interfaces, React treats each UI element as a contained state machine. It is not a "framework" in the sense of something like AngularJS. Though Facebook sometimes describes React as "the V in MVC," I find this description less helpful, since React applications needn't abide by the MVC model. React helps you build fast user interfaces that can handle complex interactions without a lot of ugly code.

If you're going to be working in React, you should be aware of these features:

React handles the DOM for you.

DOM manipulation is expensive. React's appeal comes largely from the way it solves this problem. React minimizes the amount of DOM manipulation by maintaining its own virtual DOM and only re-rendering when necessary, a feat enabled by its high-performance diff implementation.

This means that you will rarely touch the DOM directly; instead, React handles DOM manipulation on your behalf. This feature is the basis for much of React's design. If you feel like you're abusing the React API or attempting to hack your way around it, chances are you're interfering with React's understanding of the DOM.

This feature also enables built-in server-side rendering using Node.js, which allows you to easily serve SEO-friendly webpages.

React thinks declaratively, in Components.

In React, everything must subclass the Component class. Components have properties (determined by their parent) and state (which they can change themselves, perhaps based on user actions). Components should render and behave based solely on their state and properties; components are state machines. This model encourages building modular UIs and, in practice, makes it easier to work with and reason about your UI.

React marries markup to JavaScript.

Though it may feel strange to write HTML in your JavaScript, in React it's the natural way to do things. Using JSX -- plain JS mixed with HTML markup -- is optional, but I highly recommend it. React argues, and I agree, that since your markup is tightly coupled to the JavaScript that controls it, they may as well live in the same file.

Information flows in one direction.

This is more of a general React pattern than a strict rule. Information flow tends to be unidirectional in React. We'll revisit this pattern later as we begin considering how information flow needs to be handled in larger applications.

Anatomy of a React Application

To make these principles more concrete, let's look at how React works for the Codecademy learning environment.

(Click on the image to enlarge it)

Figure 1: The learning environment.

As you can see from the screenshots, the main learning environment consists of many different UI elements. Some, like the header, menu, and navigation, are present at all times. However, depending on the exercise, some components appear and disappear -- the web browser, command line, and code editor can be mixed-and-matched depending on the lesson.

The logical solution is to create React components for the various pieces. In the screenshot below, I've highlighted our main React components:

(Click on the image to enlarge it)

Figure 2: The learning environment and related components.

Each component also may contain a number of child components; for instance, the lesson panel on the left is actually composed of a number of components:

(Click on the image to enlarge it)

Figure 3: The sub-components that comprise the Lesson component.

In this case, we leverage React to determine what appears in the lesson panel. For example:

Only show the "Report a Problem" button if the user is signed in

Only render the Instructions section if this exercise has tests

Furthermore, React handles information flow between this component and others. There's a parent component for the entire learning environment, which keeps track of state such as what exercise the user is on. This parent component dictates how its children should render by setting their props (properties).

Now let's look at an example of component communication, using the following components in this much-simplified component tree:

LearningEnvironment

CodeEditor

RunButton

ErrorDisplayer

Navigation

(Click on the image to enlarge it)

Figure 4: Some of the components involved in code submission.

How do we handle the workflow of a user trying to run their code? We want to run tests against their code and either display an error message or allow them to proceed. Here's one possible flow:

When the user clicks on the RunButton, it informs its parent, the CodeEditor, of the action via callback.

The CodeEditor then informs its parent, the Learning Environment, through another callback, passing along the user's current code.

The LearningEnvironment runs tests against the user's code.

Based on the result...

The LearningEnvironment sets the errorMessage prop on the CodeEditor, which then sets the errorMessage prop on its child, the ErrorDisplayer.

on its child, the ErrorDisplayer. If the user has passed all tests for this exercise, the LearningEnvironment sets the progress prop on the Navigation component.

The updates to the UI can be performed with a single function call. If our components are declared like this in the LearningEnvironment's render method (again, with much simplification):

render: function() { return( <div> <CodeEditor error={this.state.error} /> <Navigation mayProceed={this.state.mayProceed} /> </div>); }

Remember, React mixes JavaScript with HTML markup. In this case, the render method is defining a LearningEnvironment as something which contains both a CodeEditor and a Navigation component.

We can update the LearningEnvironment's state, which will trigger a re-render and update child components as necessary:

handleTestResult: function(currentIndex, passing, error) { this.setState({ error: error, mayProceed: passing && currentIndex === this.state.currentExercise.tests.length-1 }); }

That's it; React handles the UI updating for us gracefully and simply.

Considerations for Larger Applications

Information Flow

As I noted earlier, React doesn't necessarily adhere to a MVC model; in fact, you're free to handle information flow however you'd like, but you'll need a conscious strategy for information. Do you want to pass down chains of props to components that don't actually need them, just to reach a great-great-great-great-grandchild? If that leaf node accepts user input, how does it alert its great-great-great-grandparent to the change?

In larger applications, this can become frustrating. Even in the simple example above, how should the Run button communicate user actions to the LearningEnvironment? We need to pass callbacks around, but it's hard to write truly modular, reusable components that way.

Codecademy's solution has been to generate communication adapters that manage information flow between specific components. Instead of passing callbacks, high-level components such as the CodeEditor also receive an Adapter, which provides a single interface for important communication tasks. For example, when the CodeEditor is present, the LearningEnvironment will generate an Adapter which can emit and process events related to code submission.

This approach isn't without its own pitfalls, and I spoke about this at greater length at React Conf. The main takeaway that I've had is that, regardless of how you handle information flow up the component tree, your team ought to agree on a coherent strategy.

Integration

React is easy to get started with, but it does require some tooling to leverage it effectively in your workflow. For example, we use:

A script that watches for local changes to .jsx files, and recompiles them as necessary

A separate node.js server that handles server-side rendering

Developer tools to auto-generate new component files as needed

None of these are too complicated. Gulp is a great choice for the .jsx watcher, though we wrote ours in Go. For generating new component files we use a simple bash script, which also enforces our naming conventions. If you're using a node.js server for server-side rendering, beware; it can be difficult to force require.js to pick up changes to your React code, so we have our watcher restart the node server as necessary.

Why React?

When we were overhauling our learning environment, we had to determine what tools or frameworks to make use of; we eventually chose React, and are very happy with our choice. (More details about how we chose a Javascript framework, or lack thereof, in this talk: https://www.youtube.com/watch?v=U5yjPG5mHZ8)

Here's some of the aspects that we appreciate about React:

It's battle-tested.

React is in production use at Facebook and Instagram, so we can be confident in its performance and reliability. So far, it's served us well, and we haven't experienced any significant issues.

Components are easy to think about.

Because React deals solely with individual components, which render based on their internal state, it's easy to conceptualize what ought to be happening at any given time. Your application effectively becomes a very large state machine. This means that you can test individual pieces of the UI in isolation, as well as add new components without worrying about interfering with the rest of your application.

SEO is easy.

Because React has built-in support for server-side rendering, you can serve a mostly-complete page to search engines, which is a huge boost to SEO with very little effort required. Granted, this only works in Node; since Codecademy's main app is written in Rails, we run a separate Node server that just handles React rendering.

React is compatible with legacy code, and flexible enough for the future.

Whereas adopting an entire framework is a big commitment, you can slowly experiment with adding React to an existing codebase. Likewise, if we need to move away from React in the future, we can do so fairly easily. At Codecademy, we decided to write a new project entirely in React, to try it out and learn how best to leverage it; that worked out well, and now we use it for nearly all new UI elements. I'd recommend diving in, building some experiments, and then considering how React might fit in with your existing codebase.

Stop worrying about boilerplate.

Less time spent writing boilerplate code means more time solving interesting problems. From this perspective, React is concise and lightweight. Here's the minimum code required to create a new component:

var dummyComponent = React.createClass({ render: function() { return (<div>HTML markup in JS, what fun!</div>); } });

Short and to the point. What's not to like?

We have a community to build with.

The React community is growing rapidly. When you encounter issues, there are plenty of people to discuss them with. And because many large companies are using React in production (Facebook, Instagram, Yahoo!, Github, and Netflix, to name a few) we're in good company.

In summary

React is a lightweight, powerful, battle-tested library for building user interfaces with JavaScript. It's not a full framework, but rather a powerful tool that may well change the way you approach front-end development. We've found it to be an incredibly useful tool for our frontend development, and we're very happy with our choice. For myself, at least, working in React has dramatically impacted the way I think about writing user interfaces. I'm also excited to see React grow: now that Facebook is bringing React to mobile with React Native, I suspect the future is an exciting one.

If you want to get started with React, the tutorial is the logical place to begin. There are also plenty of posts introducing key React concepts (this slide show is one of my favorites). Go ahead -- dive in, try building something, and see what you think of React's approach to frontend development. I'm eager to hear what you think, and I'll be listening on Twitter as @brindelle.

About the Author

Bonnie Eisenman is a software engineer at Codecademy.com. A recent Princeton CS graduate, she also has a bit of a hardware habit, and enjoys working with Arduinos and musical programming in her spare time. Find her as @brindelle on Twitter.

The Web Platform has gone a long way since HTML5 was first made popular and people started looking into JavaScript as a language that could do build complex apps. Many APIs have emerged and there is an abundance of content out there about how the browser can leverage all this.

This specific article series will go a step further and focus on how these powerful technologies can be leveraged in practise, not to building cool demos and prototypes, but how real practitioners use them in the trenches. In this (post)HTML5 series, we'll go beyond buzzwords and get practical insight from experts about what has actually worked for them. We'll also talk about technologies (like AngularJS) that go a step further, and define the future of how the standards and web development will evolve.

This InfoQ article is part of the series “Next Generation HTML5 and JavaScript”. You can subscribe to receive notifications via RSS.