News

Build responsive UIs with Facebook’s React.js

From the developers of Facebook and Instagram, React.js will help you learn how to construct responsive UIs

5

It’s not often a library comes along that makes you rethink how you write JavaScript, but that’s exactly what React.js does. Next to jQuery, it’s probably the most battle-tested framework out there as it’s been used on and developed by Facebook and Instagram.

React helps you build user interfaces and is focused on the ‘V’ in MVC – the view. The concept revolves around ‘components’, which are snippets of HTML with functionality attached to them. A component keeps this functionality tied to the element, resulting in maintainable and reusable code.
One of React’s prominent features is JSX, JavaScript XML syntax.

That means that you can write HTML in your JavaScript file, no messy string interpolation or clumsy native document constructors. This sounds like a gimmick, but once you’ve started using JSX it’s hard to go back. We’re going to use it to build a front-end for a fictional social network site, so you’ll learn more of React’s concepts and how to build your own components.

Script dependencies

Head to facebook.github.io/react and download React. Create a standard HTML page and add these dependencies. React doesn’t use jQuery but we’re going to use it for some AJAX requests. Moment.js is a nice library for easy date formatting, and app.js is where our own code will live.

001 <script src=”//momentjs.com/downloads/moment.min.js”></script>
002 <script src=“//code.jquery.com/jquery-2.0.3.min.js”></
script>
003 <script src=”react-0.8.0/build/react.js”></script>
004 <script src=”build/app.js”></script>

Install React tools

React makes use of JavaScript XML syntax, so you can write HTML in your JavaScript files without worrying about string manipulation or DOM constructors. It’s a Node module, so you’ll have to install Node.js (nodejs.org) if you don’t have it already. It then watches for changes in the src folder and compiles into the build folder.

001 $ npm install -g react-tools
002 $ jsx --watch src/ build/

Create a component

The commented text at the top is what the parser looks for to transform the JSX into executable JavaScript. This is as basic a class for a component should be; all components must have a render method that either contains JSX (which our watch will transform) or build markup through React’s DOM methods.

001 /** @jsx React.DOM */
002 var User = React.createClass({
003     render: function () {
004         return (
005             <div>
006                 <UserImage source={this.state.user.
profile_picture} />
007                 <p>{this.state.user.name}</p>
008             </div>
009                 <UserImage source={this.state.user.
profile_picture} />
010                 <p>{this.state.user.name}</p>

011             </div>
012         );
013     }
014 });

Render component

We can then use the React class like an HTML tag and populate the source property by treating it like an attribute; which is how we pass properties to components. In this case we’re setting where to get the data from. The second argument is which element this component should reside within, this is sometimes called the mount node.

001 React.renderComponent(
002     <User source=”/api/user.json” />,
003     document.getElementById(‘user’)
004 );

Set initial state

Components have a fairly simple API (when compared to frameworks such as Ember) but this makes for a smoother learning curve. Components have two ways to set data: properties and states. Properties should be set by the owner of the component, but state can change within itself. getInitialState is called when a component is first initialised to set your defaults.

001 getInitialState: function () {
002     return {
003         user: {
004             name: ‘’,
005             profile_picture: ‘’
006         }
007     };
008 },

Set state

componentDidMount is fired after rendering. Once rendered, we use jQuery to make an AJAX request to our ‘service’ (remember we passed the source as a property when invoking the component). Once we have that data, we can update the component’s state with setState(). ‘bind’ sets the value of ‘this’ so we don’t have to set ‘this’ as a separate variable.

001 componentDidMount: function () {
002     $.get(this.props.source, function (data) {
003         this.setState({
004            user: data.user
005          });
006     }.bind(this));
007 },

UserImage component

This simple component can be reused anywhere a profile picture is displayed. We use normal JavaScript to set a default if a height and width property doesn’t come from the parent. An alternative way to do this is to return defaults in a method called `getDefaultProps` but for brevity we’ve simply set them inline.

Story component

Our Story component will be responsible for rendering a post. Note that because ‘class’ is a reserved keyword in JavaScript, JSX has to use ‘className’ instead (hence HTML-like). We’re also using a new Time component which we’ll write later on, but other than that the rest is just markup around properties.

001 var Story = React.createClass({
002     render: function () {
003        return (
004             <div className=”story media”>

005                 <UserImage source={this.props.author.image} width=”50” height=”50” />
006                 <div className=”media-body”>
007                     <h4 className=”media-heading”>{this.props.author.name}</h4>
008                     <p>{this.props.text}</p>
009                     <Time date={this.props.date} />
010                 </div>
011             </div>
012         );
013     }
014 });

FeedList component

The FeedList component will house all of our individual stories. First we sort the data by date and then for each story we render those stories, passing the relevant properties. Lists in React need a key (identifier), else or get an error.

001 var FeedList = React.createClass({  
002     render: function () {
003         var stories = this.props.data.sort(function
(a, b) {
004             return a.date < b.date;
005         });
006         var nodes = data.map(function (story) {
007             return <Story key={story.key}
author={story.author} text={story.text} date={story.date} />;
008         });

Render FeedList

One limitation of the render method is that it can only return one top-level element, so we’ll wrap our story components in a <div>. We can then simply reference our variable as ‘{}’, which just denotes a JavaScript expression, and render all of the sorted story components at once.

001         return (
002             <div className=”commentList”>
003                 {nodes}
004             </div>
005         );
006     }
007 });

Feed component

Our Feed component will own our FeedList and set the feed. Right now it’s not doing much but it’ll control the contents of the feed, whereas the List itself will control how that content is going to look. We’ll add functionality to get the feed data in the following steps.

001 var Feed = React.createClass({
002     render: function () {
003         return (
004             <FeedList data={this.state.feed} />
005         );
006     }
007 });

Events and requests

React encourages you to communicate between components with your own event system, so we’ll use jQuery’s. When a new story is received (either if a user writes one or you could implement server-sent events, see www.w3.org/TR/eventsource) it updates the status of the feed. We also send off a request to get the first set of stories.

Unmounting components

We set the initial feed state to an empty array (so long as it’s not referencing an undefined property). componentWillUnmount is called before a component is torn down and removed from the DOM. This is the time to remove event listeners or timeouts and do manual cleanup for the lifecycle.

setInterval mixin

A mixin shares common functionality between components. For example, you might have multiple components that need to periodically check something with setInterval. This setInterval mixin (from bit.ly/1dv56Hw) simply sets and removes setIntervals in components but will automatically clear them when the component is ready to be removed.

001 var SetIntervalMixin = {
002     componentWillMount: function() {
003         this.intervals = [];
004     },
005     setInterval: function() {
006         this.intervals.push(setInterval.apply(null, 
arguments));
007     },
008     componentWillUnmount: function() {
009         this.intervals.map(clearInterval);
010     }
011 };

Specifying mixins

Our Time component will show when the story was posted in a human-readable way, which is where Moment.js comes in. We specify which mixins we’ll be using in an array and for now just return a small bit of markup (pardon the pun). We’ll be updating the state so we render that, not ‘props’.

001 var Time = React.createClass({
002     mixins: [SetIntervalMixin],
003     render: function () {
004         return (
005             <small>{this.state.date}</small>
006         );
007     }
008 });



Moments from now

When the component first loads we receive a timestamp and use Moment’s fromNow method to give a human-readable relative time (eg a few seconds ago, one hour ago). When the component mounts, we use the mixin as if the methods belong to the component, similarly to prototypal inheritance.

001 getInitialState: function() {
002     return {
003         date: moment(this.props.date).fromNow()
004     };
005 },
006 componentDidMount: function () {
007    this.setInterval(this.tick, 60000); //1 min in ms
008 },



Ticking time

Every time ‘tick’ is called we set the new relative time using setState, so every minute for every individual instance we keep encapsulated. If the component unmounts, then the timer will clear. Most component lifecycle methods have a ‘will’ and a ‘did’ version; ‘will’ is called just before and ‘did’ immediately after.

001 tick: function () {
002     this.setState({
003         date: moment(this.props.date).fromNow()    004     });
005 },
006 componentWillUnmount: function () {
007    clearInterval(this.setInterval);
008 },

Handling submit event

Our last component will be a text input so that users can post their own updates. Our handleSubmit function is just regular JavaScript. If no text has been input then you can return without having to do anything. If text has been entered then we will push the new story to the feed and then reset the box to display the placeholder text.

001 var Update = React.createClass({
002     handleSubmit: function (event) {
003         if (!event.target[0].value) return false;
004         /* next step */
005         event.target[0].value = ‘’;
006         return false;
007     },
008 });

Async set state

setState is an asynchronous method, which means that if you attempt to read the value of the new state straight after setting, it won’t have updated. To get around this it accepts a second argument that is invoked just after the state has been triggered. In this case we trigger a new story update, complete with all the details of the story.

Rendering form

As usual, all components must have a render method. Once rendered, the form doesn’t actually have an onSubmit attribute as React performs event binding internally. Instead of returning the native event, React has ‘synthetic events’ that iron out browser inconsistencies – this means that you can get some HTML5 events working in IE 8!

Render components

Now that all of our components are written, we’ll render each one out. The mount node has to be a reference to the DOM node itself (no ‘$(‘#friends’)’ but you could do `$(‘#friends’)[0]’). If you want to render something for each class instance then you’ll have to do this in a loop.

001 React.renderComponent(
002     <Friends source=”/api/activity.json” />,
003     document.getElementById(‘friends’)
004 );
005 React.renderComponent(
006     <User source=”/api/user.json” />,
007     document.getElementById(‘user’)
008 );

More components

Self-closing tags are great but as the code demonstrates, it really is down to your own preference. React works well for many of the situations where bespoke functionality is needed and feels like a very natural way to write components. Give it a try for yourself.

×