Upcoming and OnDemand Webinars View full list

How to use Facebook’s React Library to Build UIs, Part 2

Chris Aquino

In my previous post, we created a simple UI component with Facebook’s React library. Now, we’ll create data flows between multiple components.

Rot13 encoder app

Let’s build an app that takes the input from a textfield and displays it as rot13.

We’ll structure the app like so:

Data flow diagram for rot13 app

And, the components we’ll build are:

  • Input, for receiving text input.
  • Output, to display the encoded output.
  • App container, which encapsulates the other two components.

It may not be obvious right now, but the container is essential. Every React component must return a single element. In order for us to display both the Input and the Output components, they need to be inside another component.

Laying out our components

We’ll start with our boilerplate HTML, making sure to include the libraries for React and for the JSX Transformer. Remember, when you work with JSX, you need to include the /** @jsx React.DOM */ directive and specify the script’s type as text/jsx.

For each component, we pass in a “spec” object that has a render function.

<!DOCTYPE html>
<html>
  <head>
    <script src="http://fb.me/react-0.10.0.js"></script>
    <script src="http://fb.me/JSXTransformer-0.10.0.js"></script>
  </head>
  <body>
    <div id="container"></div>
    <script type="text/jsx">
      /** @jsx React.DOM */
      var InputComponent = React.createClass({
          render: function () {
          }
      });

      var OutputComponent = React.createClass({
          render: function () {
          }
      });

      var AppComponent = React.createClass({
          render: function () {
          }
      });

      React.renderComponent(
        <AppComponent />,
        document.getElementById('container')
      );
    </script>
  </body>
</html>

Next, we need the render function of our components to return values.

  var InputComponent = React.createClass({
      render: function () {
          return (
              <input></input>
          );
      }
  });

  var OutputComponent = React.createClass({
      render: function () {
          return (
              <div></div>
          );
      }
  });

  var AppComponent = React.createClass({
      render: function () {
          return (
            <div>
                <InputComponent />
                <OutputComponent />
            </div>
          );
      }
  });

  React.renderComponent(
    <AppComponent />,
    document.getElementById('container')
  );

Notice that the AppComponent returns a <div> with the InputComponent and OutputComponents nested inside of them. When the AppComponent’s render function is called, the render functions of the InputComponent and OutputComponent are called as well. This produces a DOM tree roughly equivalent to:

<div>
    <input />
    <div></div>
</div>

One-Way Data Flows

We need a way to get data from one component to another. React has a simple, but sophisticated, system for moving data around. Here is how we might get the static text “Ni hao, React” from our AppComponent to our OutputComponent:

  var InputComponent = React.createClass({
      render: function () {
          return (
            <input></input>
          );
      }
  });

  var OutputComponent = React.createClass({
      render: function () {
          return (
              <div>{ this.props.value }</div>
          );
      }
  });

  var AppComponent = React.createClass({
      render: function () {
          return (
            <div>
                <InputComponent />
                <OutputComponent value="I know kung fu" />
            </div>
          );
      }
  });

  React.renderComponent(
    <AppComponent />,
    document.getElementById('container')
  );

Our AppComponent provides a value attribute to the OutputComponent. Then, the OutputComponent accesses this attribute as a property of this.props.

This is an example of React’s one-way data flow. Every component has access to a props object, and those properties are added to this props object by the parent component. The property names can be any valid JavaScript variable name, and the value can be any valid JavaScript value or expression.

Here, we named the property “value,” but it could have easily been “stringToDisplay” or “outputText.”

To access the value in the OutputComponent’s JSX, you wrap it in single curly braces. This tells the JSX Transformer to evaluate whatever JavaScript expression is inside those curly braces.

State

The values passed from a parent component to a child component do not have to be static. The first step is the let the AppComponent maintain its own store of dynamic data. A state variable, like the props variable, is available to every component. But state is the data that is owned by a component, as opposed to being provided by a parent component.

Let’s update the code so that the AppComponent sets up an initial state object for holding data, and passes some of that data to the OutputComponent.

  var AppComponent = React.createClass({
      getInitialState: function () {
          return {
              value: "I know kung fu!!!!",
          }
      },
      render: function () {
          return (
            <div>
                <InputComponent />
                <OutputComponent value={ this.state.value } />
            </div>
          );
      }
  });

  React.renderComponent(
    <AppComponent />,
    document.getElementById('container')
  );

This code has the equivalent outcome as the previous version, but reveals a little bit more about how to use React effectively. The getInitialState function is called automatically and sets up the state object for a component. We can return any object, and are specifying an object literal for the sake of simplicity.

To pass some of our AppComponent’s state data to the OutputComponent, we use attributes, as we did before. But this time, instead of a static string value, we use the expression this.state.value, wrapped in single curly braces.

Note that we do not wrap { this.state.value } in quotes. If we did that, it would be passing a string, and not the actual JavaScript value we intended.

Events

It’s time to complete our data flow by connecting our InputComponent’s input element to our AppContainer’s state. First, we will watch for the change event on the input.

  var InputComponent = React.createClass({
      _changeHandler: function (event) {
          console.log(event.target.value);
      },
      render: function () {
          return (
            <input
              onChange={ this._changeHandler }
            ></input>
          );
      }
  });

Again, we make use of attributes to declare how our components should interact. Technically, the <input> element is a React component, and we are specifying its onChange attribute. We pass it this._changeHanlder as the value, and we declare a _changeHandler property as part of our component’s spec.

onChange is an attribute that is baked into React. You can find a list of supported event attribute names in the Event System documentation on the React site.

Alternatively, _changeHandler is a name that we made up arbitrarily. One convention that is popular in the React community is to prefix your custom functions with an underscore. This is a good way to communicate that this is a function that you have declared, and is not one of React’s built in component functions.

We define _changeHandler just like any other DOM event handler callback; it should accept a parameter for the event object. Currently, it is only logging the value of the input. To make it do something more interesting, we’ll need to make a couple of changes to the AppComponent.

We will update AppComponent by adding a function that updates its state, and we will pass a reference to this function to InputComponent.

  var AppComponent = React.createClass({
      getInitialState: function () {
          return {
              value: "I know kung fu!!!!",
          }
      },
      _updateValue: function (newValue) {
        this.setState({
            value: newValue
        });
      },
      render: function () {
          return (
            <div>
                <InputComponent sendChange={ this._updateValue }/>
                <OutputComponent value={ this.state.value} />
            </div>
          );
      }
  });

We have created an _updateValue function that calls the built in setState component function. It accepts an object whose properties will overwrite the existing state. When you call setState, you only have to specify the ones you want to update, even if your component’s state has many other properties.

We declare an attribute for InputComponent named sendChange and give it a reference to _updateValue. InputComponent now has access to this function reference in its props object. Let’s use that to complete the data flow.

  var InputComponent = React.createClass({
      _changeHandler: function (event) {
          this.props.sendChange(event.target.value);
      },
      render: function () {
          return (
            <input
              onChange={ this._changeHandler }
            ></input>
          );
      }
  });

Now, when you type into the input field, output updates immediately. Right now, it’s in plain text. Let’s fix that.

And…ROT13!

We need to add our rot13 function to the OutputComponent and call it in our render function.

  var OutputComponent = React.createClass({
      _rot13: function (s) {
          return s.replace(/[a-zA-Z]/g,function(c){return String.fromCharCode((c<="Z"?90:122)>=(c=c.charCodeAt(0)+13)?c:c-26);});
      },
      render: function () {
          return (
              <div>{ this._rot13(this.props.value) }</div>
          );
      }
  });

Now, our output, though not anywhere near NSA-proof, is unreadable by normal humans. Hooray!

React Components act as neat little boxes that take some input and produce a piece of your UI. Each one has limited responsibilty, which makes it easier for you to reason about your app. While building this app, you learned how to pass data and function references using props and state. Then you made these data flows reactive via Event attributes. Next time, we’ll work on an even larger application and examine the benefits of React over other frameworks.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project