Contents


React: Create maintainable, high-performance UI components

Go beyond the browser DOM with the React JavaScript library for maintainable web UIs

Comments

JavaScript — supported by all modern browsers and indisputably the web's de facto native programming language — has direct access to all the richness that the web-as-a-platform offers. Increasingly, complex JavaScript projects have grown beyond the realm of the solitary coding whiz to large teams of collaborating developers. This shift brings with it an entourage of related problems. Working code based on quick hacks becomes impossible for others to comprehend and maintain. A new recruit can take a long time to come up to speed on a complex system. An ever-growing code base can't be reused among the team at large. And elusive, erratic performance anomalies often occur only in live systems.

No one is more familiar with these problems than vanguard social networks that must deliver competitive yet complex web UIs that millions use daily. In 2013, Facebook/Instagram released the React project — a UI component-building library — as open source, providing a glimpse into how the company deals with these growing pains. React and its associated best practices were enthusiastically embraced by JavaScript developers — who incidentally also find themselves sympathetically working through the same pain points.

This tutorial introduces React, explains how it works, describes the prescribed best practices, and gets you coding quickly with examples. (See Download to get the sample code.) When you're done, you'll be able to create your own reusable React components or assemble new ones out of the hundreds of community-contributed open source components.

React for creating reusable UI components

You declaratively specify your web UI components hierarchy and feed it to React's virtual DOM. Then React takes care of the synchronization of your UI with the browser's actual DOM at the appropriate time.

React is delivered as a JavaScript library with a JSX compiler and associated developer tools. React facilitates the creation of reusable high performance UI view components that can be composed to form modern web UIs. By following the prescribed best practices, you can craft these components in a maintainable and reusable way.

To optimize runtime performance, React components are first rendered into a managed virtual DOM, as shown in Figure 1.

Figure 1. React operations
Diagram of Reaction operations
Diagram of Reaction operations

You declaratively specify your web UI components hierarchy and feed it to React's virtual DOM. Then React takes care of the synchronization of your UI with the browser's actual DOM at the appropriate time. React's virtual DOM implementation is self-contained and does not depend on a browser; it can even be used for server-side rendering (see Related topics). The virtual DOM performs an optimized diff of its internal state against the browsers' DOMs and performs the minimal updates required to keep the UI consistent.

This retained-mode approach (see the Retained-mode operations sidebar) bypasses a large class of performance problems associated with directly modifying the browser's DOM elements one visual transition at a time (as in the operation of popular UI component libraries such as jQuery UI).

Unlike similar libraries, React does not enforce the conventional Model-View-Controller (MVC) style of UI construction and state management. Instead, React focuses solely on construction of views, and the project has shared some opinions on why MVC might not be best for complex web UI creation (see the Pitfalls of MVC for complex modern UIs sidebar). However, nothing in React prevents the use of MVC construction; many developers' initial experience with React involves integrating new code into the view layer of existing large MVC-based projects.

Motivations to try yet another JavaScript UI library

Some desirable characteristics of React include:

  • React is easy to learn; if you're proficient with JavaScript, you can start working with the library in a single afternoon.
  • Proven best practices prescribe component-building wisdom that can make large JavaScript code bases maintainable and manageable.
  • React works with most loaders and toolbelts — AMD, CommonJS, or globals; gulp, bower, npm, browserify, webpack, grunt — to cater to modern JavaScript developers' every whim.
  • Adopting React isn't a risky all-or-nothing proposition. The library plays well alongside most existing code bases (even legacy MVC ones) and integrates well with existing UI frameworks (see Related topics).
  • React's design mates perfectly with asynchronous back-end architectures to accommodate future technologies.
  • The React runtime focuses on performance yet is extensible to other target platforms, including (via React Native) iOS and Android.

Unifying declarative UI description and logic with JSX

JSX is a transformer and compiler that accepts an extended JavaScript syntax. JSX translates extended HTML-like notation like this:

<div>
     <MyLabel  text={TextLabel} />
     <MyTextfield />
     <MyButton textlabel='OK' />
</div>

...into JavaScript React API calls like this:

React.createElement("div", null, 
     React.createElement(MyLabel, {text: TextLabel}), 
     React.createElement(MyTextfield, null), 
     React.createElement(MyButton, {textlabel: "OK"}))

JSX's familiar HTML-like notation greatly simplifies coding and maintenance of React components. This simplification is especially evident for declaring deeply nested component relationships.

JSX enables you to co-locate the UI logic together with related structural description all in a single file, which helps to increase productivity and reduce errors in large projects. With other frameworks, up to three times the number of files might need to be synchronized: template file, handler JavaScript code file, and the structural HTML description.

Deploying JSX

JSX can run in-browser or independently. The in-browser transformer helps in development because you can instantly see the results after you modify your JSX code. For production, you definitely want to incorporate the stand-alone transformer into your build toolchain for the best performance.

All of this tutorial's examples (see Download) use the in-browser transformer for simplicity. The first example (example1) fetches both React and the JSX transformer from a content delivery network (CDN) and is the easiest way to get up and running (place the files behind a web server and access their URLs):

<html lang="en">
<head>
<script src="//fb.me/react-0.12.2.js"></script>
<script src="//fb.me/JSXTransformer-0.12.2.js"></script>
<script type="text/jsx" src="example1.jsx"></script>
</head>
<body>

example2 and example3 use the bower package manager to install React; they refer to locally fetched copies of the in-browser JSX transformer. For instance, example2.html fetches the two files via:

<script src="bower_components/react/react-with-addons.js"></script>
<script src="bower_components/react/JSXTransformer.js"></script>

In both cases, you can experiment with React by modifying your JSX files and refreshing the browser to see the result.

Creating custom React components

Now quickly create a component with React, using the code in example1.jsx:

var MyTextfield = React.createClass({
  render: function() {
    return <input type='text' />;
  }
});

That's it.

This simple custom React component, shown together with other React components in Figure 2, renders an uncontrolled input field, wrapping an underlying HTML <input> element. This component can now be used as <MyTextfield>. Note that native HTML element names start with a lowercase letter, and custom React component (class) names begin with an uppercase letter.

Figure 2. <MyTextfield>, <MyLabel>, and <MyButton> React components
Screenshot of first React component
Screenshot of first React component

Props in React components and JavaScript expressions in JSX

The second custom component in example1 is <MyButton>:

var MyButton = React.createClass({
  render: function() {
    return <button>{this.props.textlabel}</button>;
  }
});

<MyButton> renders a customized HTML <button> element. This component demonstrates incoming properties (called props in React lingo). Props are incoming arguments to a component; they are exactly like attributes on an HTML tag. The component can access these attributes to customize its rendering via this.props. JSX's support for evaluating inline JavaScript expressions via { JavaScript expression } can be used to render property values.

In Figure 2, the <MyButton> instance is parameterized with OK on its textlabel property: <MyButton textlabel='OK' />.

Composing React components

Composition is the cornerstone of reuse for UI components. React enables you to easily compose existing React components, along with native HTML elements, to build more-complex ones.

In example1, the custom React components —<MyTextfield>, <MyButton>, and <MyLabel>— are composed together in the UI. JSX makes this straightforward and simple:

React.render(
  <div>
    <MyLabel  text={TextLabel} />
     <MyTextfield />
     <MyButton textlabel='OK' />
 </div>,
  document.getElementById('container')
);

The preceding API call renders the composed components into the <div> element with the ID of container— within the virtual DOM.

Function as prop and React's SyntheticEvent

Attributes on elements are often parameterized with callback functions instead of simple data types. For instance, you probably have experience setting the onClick attribute of an HTML <button> element. The same technique can be applied to React components. In the second example, example2.jsx, the custom <MyButton> React component takes an onClick handler callback as one of its properties:

var MyButton = React.createClass({
  _buttonClicked:  function(e) {
    if (this.props.onClick) {
        this.props.onClick(e)
    }
  },
  render: function() {
    return 
     <button onClick={this._buttonClicked}>
             {this.props.textlabel}</button>;
  }
});

When you click the button, the HTML <button> element's onClick event is fired, and React forwards the event to <MyButton>'s _buttonClicked() implementation.

It's important to know that _buttonClicked() is called not with a native browser DOM event but with an instance of React's own cross-browser, W3C-compliant SyntheticEvent object. SyntheticEvent has uniform behavior across different browsers and wraps the actual DOM's raw event. (The wrapped DOM event is still accessible via the nativeEvent property; see Related topics for a link to more information on SyntheticEvent).

In _buttonClicked(), the code ensures that <MyButton>'s own onClick property is set and then forwards SyntheticEvent to that event handler.

Maintaining state in React components

Some components need to maintain an internal state that's used during rendering. For example, a check box component requires a state to remember that it was selected.

In example2.jsx, the <MyTextfield> component maintains internal state (a variable named data) that always reflects the current numeric value entered into the text field. To provide an initial value to a state variable, implement getInitialState(). In <MyTextfield>, data is initialized to 1:

var MyTextfield = React.createClass({
  getInitialState: function() {
     return {
      data: '1'
     };
  }, 
...

React component state is accessible via this.state. And this.state.data is used to render the value of the underlying <input> element at all times:

render: function() {
  return <input type='text' onChange={this._entryChanged} 
                value={this.state.data} />;
}

This example is a typical pattern for constructing React components: a component's state and prop values are used to render or customize the component.

In the case of this <MyTextfield>, the underlying <input> element is controlled, because its value is always rendered by the React component (as compared to the uncontrolled version of <MyTextfield> in example1).

Trying example2: A dynamic table generator

To better appreciate how <MyTextfield> and example2 work, try out the example. Place the example2 files behind a web server and access example2.html. Figure 3 shows what you see initially. The number of rows is set to 1 because the initial value of this.state.data is 1.

Figure 3. Custom dynamic table generator component
Screenshot of custom dynamic table generator component
Screenshot of custom dynamic table generator component

You can type any value between 1 and 9 into the text field; it won't accept anything else. When you click the OK button, the specified number of rows is generated in the table shown on the right side. Figure 4 shows 9 rows generated.

Figure 4. Table generator showing nine rows
Screenshot of custom dynamic table generator component with nine rows
Screenshot of custom dynamic table generator component with nine rows

When you enter values into the text field, the onChange event of <input> is fired, and React routes the event to <MyTextfield>'s _entryChanged() handler:

_entryChanged: function(e) {
    var val = e.target.value;
    var validated = '1';
    if (!(isNaN(val) || (parseInt(val) > 9 ) || (parseInt(val) < 1)))     {
      validated = val;
    } 
    this.setState({data: validated});
    if (this.props.onChange) {
      this.props.onChange(validated);
    }
  }

_entryChanged() ensures that the entered value is a number between 1 and 9 inclusive. Any entered value outside of the range is set to 1. _entryChanged() then updates the state (data) using this.setState(). If data is set to a new value, the text field content will be updated the next time render() is called by React. As a result, the content of <MyTextfield> is always in sync with the data state variable.

This behavior is another pattern frequently observed in React components: Event handlers modify state variables, which in turn change the appearance of the rendered component.

A <DynamicList> React component is responsible for rendering the table rows (see the source code for details), depending on a rows property that is supplied. Each cell in the table is a <MyTableCell> instance that wraps an <input> element that has been disabled.

Last but not least is a <ListCreator> component that composes <MyTextfield>, <MyButton>, and <DynamicList> to create the final UI. When the <MyButton> instance is clicked, the _okClicked() handler copies its state value between data (which tracks the value in <MyTextfield>) and rows (which is used to render rows in <DynamicList>). This state update in the event handler causes <DynamicList> to rerender its displayed rows.

Rendering optimization with shouldComponentUpdate and PureRenderMixin

Rendering in the virtual DOM is already fast. Nevertheless, it's still wise to avoid rerendering parts of the UI that don't change between rendering passes.

React has a callback named shouldComponentUpdate that any component can implement. A component can prevent unnecessary rendering by returning false— if it knows for sure that no change occurred since the previous render. By default, it always returns true.

The <ListCreator> component in example2 uses a PureRenderMixin add-on from React:

var ListCreator = React.createClass({
  mixins: [React.addons.PureRenderMixin],
  ...

The mixin adds an implementation of shouldComponentUpdate that shallowly compares all the previous props and state values against the current ones. If it detects no change, it returns false— eliminating unnecessary rendering. If the appearance of a component is totally dependent on props and state values, as in <ListCreator>, the mixin can help to optimize rendering performance.

Using third-party reusable React components

One advantage of a large and active React community is the abundant availability of ready-to-use React components. Casual search on open source repositories such as GitHub turns up hundreds of components for exploration.

In example3, a third-party bar-chart-drawing React component (react-bar-chart, created by Mateus Zitelli) is used to create the UI. This example displays page-visit statistics for a website, categorized by age group. Figure 5 shows the UI.

Figure 5. An updatable bar chart component
Screenshot of the react-bar-chart component
Screenshot of the react-bar-chart component

You can edit each of the cells in the table on the left (click the cell or the text field), and the bar chart immediately refreshes to show the updated value.

Table 1 describes the custom components in example3 and can act as guide for you to explore the example3 source code on your own. The components are distinctly labeled in Figure 5.

Table 1. React components in the updatable bar chart example
React componentDescriptionImportant state/props
<MyTableCell>An updatable table cell. Combines a controlled <input> with a <button>. The <input> is disabled and the <button> is set to {display: none} when the cell value is not being edited.editing: tracks if the cell is being edited.

data: always reflect the value displayed in the cell.
<MyTable>Multiple-rows table containing an instance of <MyTableCell> in each row. Stateless. data is a prop, an array of hash, containing text and value to populate each row. onUpdate is a callback prop, fired after an editing session, that forwards the changed cell information.
<MyBarChart> A controller-view. Renders an instance of <BarChart> via third-party React component. Interfaces with Flux store to receive notification when site statistics change. stats: an array of hash, containing text and value; supplies data for chart.
<MyTableWrapper>A controller-view. Interfaces with Flux dispatcher, sends sitestats update action whenever a cell value is modified in the table.sitestats: an array of hash, containing text and value; tracks current values in table.
<EditableChart>Composes <MyBarChart> and <MyTable> into the final UI. Stateless. data is a prop that supplies initial data, fetched from Flux store, to be displayed in the composed components.

Accessing rendered elements and the physical DOM

Most of your code works only with the virtual DOM. However, some situations demand access to the actual browser DOM. One example occurs when you need to integrate with existing libraries such as jQuery Mobile (see Related topics) and D3; another is in the _cellClicked() handler in <MyTableCell>. After you click the cell, the cell goes into editing mode and places focus on the actual <input> browser DOM element. You do this via a delayed call to getDOMNode():

setTimeout(function() {
        this.refs.inp.getDOMNode().select();
      }.bind(this), 100);

refs is used to access elements that are created during rendering in the virtual DOM (similar to getElementById() in the browser DOM).

Transferring properties with JSX spread attributes

In <MyTableWrapper>, all its props are transferred to the wrapped <MyTable> instance. However, the onUpdate prop value must be overridden and point to its _dataUpdated callback. You do this by using JSX spread attributes notation:

<MyTable {...this.props} onUpdate={this._dataUpdated}/>

Default values for properties

In <EditableChart>, the initial default value for its sitestats prop is fetched from a Flux store (see this tutorial's Flux: Extended architecture for React apps section). Use getDefaultProps to supply this initial default value. React caches the value for subsequent use:

  getDefaultProps: function() {
      return {
        // initial default value only - cached by React
        sitestats: MockSiteStatsStore.getSiteStats()
      }
  }

Styling React components

Use regular CSS to style your app. For example, example3.css contains CSS3 flexbox code to style the app:

.containing{
    display: -webkit-flex;
    display: flex;
    width: 100%;
    height: 600px;
}

.left-side {
    width: 300px;
    -webkit-flex: none;
    flex: none;
}

.right-side {
    -webkit-flex: 1;
    flex: 1;
}

Note the use of the className prop in <EditableCell> to specify the CSS class (to avoid JSX keyword clash):

<div className="containing">
 <div className="left-side">
   <MyTableWrapper data={this.props.sitestats} />
 </div>
 <div className="right-side">
   ...
  </div>
</div>

If you have styles that might change with the internal state of a component, you can also style it in JavaScript with inline styling. <MyTableCell> includes a native <button> that is styled via inline JavaScript:

  <button onClick={this._okClicked} 
    style={this.state.editing? {zIndex: 5}: {display:'none'}}>
      ok</button>

Flux: Extended architecture for React apps

Flux is one approach to structuring apps that use React components. It prescribes a one-way data-flow architecture that can eliminate problems associated with interconnected MVC networks found in complex UIs and improves the long-term maintainability of a code base.

In a nutshell, shared mutable application state used in component rendering is pushed upstream. Controller-views (upper-level owner views — such as <MyBarChart> and <MyTableWrapper> in example3 — that manage state to be rendered by owned subviews) replace traditional controllers. Traditional model operations are now performed by routing actions to interested stores through a singleton dispatcher. Actions are data bundles that declare the operation to be performed and its associated data. (Tightly coupled method invocations are transformed into loosely coupled data flow by actions.) Stores and their processing interdependencies are handled in a loosely coupled manner through the dispatcher. (Practitioners of design patterns might see similarities to the Command and Chain of Responsibility patterns; system engineers might be thinking marshaling and serialization.)

Controller-views never share application state directly or indirectly with one another. They register their interest in data changes with stores, and stores notify views to fetch data (and update their own managed state) when change occurs. This is the only flow of data into these views. Figure 6 illustrates this one-way data flow.

Figure 6. One-way data flow in Flux
Diagram of Flux one-way data flow
Diagram of Flux one-way data flow

The example3 code loosely follows the Flux pattern in construction, with only a mock store implementation in place. <MyBarChart> registers with MockSiteStatsStore for any data update. Notification from the store is the only way that <MyBarChart>'s managed state can ever change.

Any interactive data change made in <MyTableWrapper> is flowed as an action through to the dispatcher/store via a MockSiteStatsAction.update() call. Even though the two components sit side-by-side within the same UI, they do not share states directly. One-way data flow is from <MyTableWrapper> through the dispatcher, to the interested MockSiteStatsStore, then via change notifications back to <MyBarChart>.

Detailed examination of the Flux application building pattern is outside the scope of this article. See Related topics to explore further.

Available tools for working with React

React Developer Tools is a useful Chrome browser extension available through the Chrome web store. When debugging with Chrome Devtools, you can view the React components hierarchy of an application instead of the more cryptic browser DOM representation. Figure 7 shows how the example3 components hierarchy looks with React Developer Tools installed.

Figure 7. React Developer Tools showing components hierarchy
Screenshot of components hierarchy in React Developer Tools
Screenshot of components hierarchy in React Developer Tools

A profiler is also available as an add-on to instrument your React code.

Into the future of the React ecosystem

Both the Relay extension to Flux and React Native were announced at the React.js Conf in January 2015. As of this writing, neither has been made available as open source.

Relay

Relay extends Flux to include server data fetching. The key idea behind Relay is to enable each React component to specify its own data-fetch requirements independently. Typically, this refers to the data that is used to render the component itself. Relay will enable static declaration of a component's data requirement within the same (JSX) file where the UI logic resides. This significantly cuts down typical file clutter when you're working with complex full-stack applications. It also allows immediate verification of correctness and synchronization during development by refreshing the browser after editing the JSX file.

The enabling technology behind Relay is GraphQL, a composable declarative query-specification language for arbitrarily shaped data. (Facebook has been using GraphQL in production for a couple of years.) GraphQL enables higher-level React components to compose the requirements of its owned components — without depending on the details of the requirements themselves — and build a combined query that can be sent to the server.

Results from GraphQL queries are stored into a general common Flux store, where views with registered interest are notified for update.

React Native

React Native substitutes the browser DOM for the Android or iOS platform. It will enable React developers to target their apps for mobile devices.

Under React Native, JavaScript code runs on its own system thread using a native interpreter. A so-called high-performance asynchronous batched bridge connects your React code to the native platform — orchestrating native UI components to implement your UI. You can also expose custom native methods for JavaScript access through this bridge. HTML elements in React are replaced by native-code views and UI widgets on the mobile platform. React Native will support styling of native components via a subset of CSS over JavaScript.

According to the development team, applications created using React Native can reproduce fine and subtle UI nuances that users associate only with native apps — a claim that WebViews- or HTML5-based alternatives currently can't match.

Conclusion

JavaScript developers can pick and choose from a huge variety of open source frameworks and libraries when creating UIs for their projects. But few have been battle-tested in complex performance-sensitive UI projects; fewer have sophisticated tool support; yet fewer come with proven best practices for coding and design, and a guaranteed future growth path that extends to server-side data operations and mobile development. React offers all this and more, with growing, vibrant community support. There's no better time than now to add React to your development toolbox.


Downloadable resources


Related topics

  • React: Visit the official React website to get the latest version and read the reference docs.
  • developerWorks Premium: Provides an all-access pass to powerful tools, curated technical library from Safari Books Online, conference discounts and proceedings, SoftLayer and Bluemix credits, and more.
  • Flux: Discover the details of Flux (and, soon, Relay) as the extended application design architecture that plugs and plays with your React components.
  • Tooling Integration: See how to use precompiled JSX for the best performance in production deployment environments.
  • SyntheticEvent: Learn more about React's SyntheticEvent implementation and see how events propagate through React components.
  • jquery-bootstrap: Start integrating React into your existing projects. This example from the React team shows how to add React into your jQuery-Bootstrap projects. Or if you are already using the jQuery Mobile UI, this example adds React support.
  • React Profiler: Profile the performance of your React code with the profiler add-on.
  • React.js Conf videos: Check out these videos of the React team introducing Relay, React Native, and other related technologies in the React ecosystem.
  • React: Get the React source code from the project's open source GitHub repository.
  • Get the latest update of the example code for this tutorial from the author's repository.

Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Open source
ArticleID=998799
ArticleTitle=React: Create maintainable, high-performance UI components
publish-date=02272015