After working through the first part, we have a simple app that writes “Hello World” to the page. Before we add new functionality, let’s change our app to reflect how many people write real React apps. To do that, we will start using JSX and we will integrate some features from ES2015.
Converting to JSX
JSX is an extension to JavaScript that allows writing XML inline with our code. This succinctly expresses how we build the virtual DOM and takes advantage of our experience writing HTML. JSX has its own extension, so let’s go ahead and move our file.$ mv app/main.js app/main.jsxWe will also have to change our infrastructure to handle the new file type. Webpack does not naturally handle JSX files, but there is a loader which does. Let’s install it and the style loader we will be using.$ npm install babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0 --save-dev$ npm install style-loader css-loader --save-devOur config file needs a few changes to tell Webpack to use this new loader.module.exports = {
entry: {
app: ['./app/main']
},
output: {
path: './app',
filename: 'bundle.js'
},
resolve: {
extensions: [ '', '.js', '.jsx' ]
},
module: {
loaders: [{
test: /\.jsx?$/,
loader: 'babel',
query: {
presets: ['es2015', 'react', 'stage-0']
}
}, {
test: /\.css$/,
loader: 'style-loader!css-loader'
}]
}};
The first thing to note here is that we tell Webpack to resolve file extensions in a specific order. We remove “.js” from line three and Webpack will now find our moved file.The second important change is specifying the Babel loader under the module key. Now Babel transforms any file that ends with “.jsx” before Webpack’s module loader. We are now loading styles as well.Let’s check out how JSX changes the code.var React = require('react');
var ReactDOM = require('react-dom');
var HelloWorld = React.createClass({ render: function () { return <div>Hello World</div>; }});
ReactDOM.render(<helloworld>, document.querySelector('#react-mount'));</helloworld>
Compared to before, we replaced all calls to React.createElement with an XML element. That is all JSX does. In the last article, we showed this example for building a Bootstrap form input.React.createElement('div', { className: 'form-group' },
React.createElement('label', { htmlFor: 'exampleInputEmail1' }, 'Email address'), React.createElement('input', { type: 'email', className: 'form-control', id: 'exampleInputEmail1', placeholder: 'Email' }));
// <div class="form-group"></div>
// <label for="exampleInputEmail1">Email address</label>
// <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">
//
Here is how it looks in JSX.<div classname="form-group"></div>
<label htmlfor="exampleInputEmail1">Email address</label>
<input type="email" classname="form-control" id="exampleInputEmail1" placeholder="Email">
Note that just like with React.createElement, we have special attributes for htmlFor and className. Also worth recognizing, this is XML and not HTML. In HTML, input is a void element and we do not need to close it. This means <input type="email" classname="form-control" id="exampleInputEmail1" placeholder="Email"> is valid by itself. Within JSX, we must close every element. In this case, we make it a self-closing tag by ending it with />.
Using ES2015
The next big infrastructure change we will take on is converting the app to use some new ES2015 features. ES2015 is the next generation of ECMAScript. It introduces some of the largest changes to the language to date. These additions help us write cleaner, safer code. Here is how our code looks upgraded to the new syntax.import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class HelloWorld extends Component {
render() {
return <div>Hello World</div>;
}
}
ReactDOM.render(<helloworld>, document.querySelector('#react-mount'));</helloworld>
Lucky for us, the infrastructure needed to support writing in ES2015 is already done. Babel polyfills the new language features we will be using. Let's begin breaking down some of what we gain from the new language extensions.import React, { Component } from 'react';ES2015 introduces a module system for the first time to the language. This provides a standard way to use code from other files. No more need to include a CommonJS or AMD style loader.class HelloWorld extends Component
Another important feature is the new class system. Classes are a way to organize code that can make it easier to write reusable pieces.const message = 'Hello World';
message = 'Hi World';
//=> TypeError: assignment to constant variableif (true) { let scopedMessage = 'Hello World'; console.log(scopedMessage);
//=> Hello World}console.log(scopedMessage);
//=> ReferenceError: scopedMessage is not define
There are also two new ways to define variables. With const, the value cannot be changed. This will help us reduce bugs by limiting where a value can be changed. The other, let, allows us to define something within the block scope, instead of the lexical scope.const message = [{ word: 'Hello' }, { word: 'World' }];
people.map((message) => message.word);
//=> [ 'Hello', 'World' ]
We will also get a lot of usage from the new closure syntax. This short form reduces the boilerplate needed to write functional code.
Extending the App
We have Hello World written in JSX using ES2015. To explore more of React.js, let’s take this code base and begin adding to it. Instead of greeting the world, we want to be able to display any message — our very own Twitter clone.Here is the design for our app:
To show a message, we will pass the text into the component as a property. You’ve already used properties. They are the attributes on the XML element. You can access them off the props object of the component. Let’s create a directory for our components, add some libraries, and then get to work!$ mkdir app/components$ npm install moment --save-devMoment is a library for working with JavaScript Dates. It makes handling time easy, and we'll need it in order to complete the design.Create a file, app/components/message.jsx, and add the following content.import React, { Component, PropTypes } from 'react';
import moment from 'moment';
export default class Message extends Component {
static propTypes = {
author: PropTypes.string,
content: PropTypes.string,
createdAt: PropTypes.instanceOf(Date)
};
componentDidMount() {
this.interval = setInterval(() => {
this.forceUpdate();
}, moment.duration(1, 'minute').asMilliseconds());
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
return (
<div classname="message"></div>
<div classname="message__author-line"></div>
<div classname="message__author">{this.props.author}</div>
<div classname="message__created-at"></div>
{moment(this.props.createdAt).fromNow()}
<div classname="message__content"></div>
{this.props.content}
)
}
}
There are several new things to look at here. Let’s break it down.The first bit is export default. This is how the ES2015 module system defines what other files can import from this one. Everything within the file is private unless you explicitly tell the system to export it. The default keyword means this is what gets imported with import Message from ‘./message’. You can also export named values.export function bar() {
console.log('bar');
}
In this case, we would import the value as:import { bar } from ‘./foo’;
The next bit we added are the static propTypes. Static properties didn’t make it into the ES2015 spec, but their draft is on pace for the next one. These are properties of the class itself, not of its instances. In this case, we define what properties the component needs. Although these are not required to use props, it is best practice to include them. They serve as reference and provide some validation. There are many available PropTypes to fit your needs.Also, we see introduced here two more of the component lifecycle functions. During the lifecycle, componentDidMount triggers after React adds the component to the page. It is a good place to put setup logic. For example, many widget libraries like Chosen or Scribe take an element and then enhance it. This is where you would tie those libraries in with your React components.We also see a second new lifecycle method, componentWillUnmount. It fires shortly before React removes a component from the page. Here you would write teardown code to prevent memory leaks.Within render you see a feature of JSX we’re using for the first time. In JSX, we escape JavaScript code with braces. The code can be any JavaScript, it is not a template language. In this case, you see us accessing properties off the props object.The next step is including our component in app/main.jsx and rendering it to the screen.
Adding User Interaction
The next part we will handle is user input. We need to allow the user to add messages. To do that, let’s create a message box and a button for creating the message. In app/components/message-creator.jsx:import React, { Component, PropTypes } from 'react';
export default class MessageCreator extends Component {
static propTypes = { create: PropTypes.func };
createMessage(e) {
e.preventDefault();
const contentNode = this.refs.content.getDOMNode();
this.props.create(contentNode.value);
contentNode.value = '';
}
render() {
return (
<form classname="message-creator" onsubmit="{this.createMessage.bind(this)}"></form>
<textarea classname="message-creator__content" ref="content" required="{true}"></textarea>
<input classname="message-creator__post-button" type="submit" value="Post">
)
}
}
Much of this should be familiar now. Do notice the onSubmit handler. We create the message using a ref. You can find the attribute on the textarea element in the render function. Refs are how React allows you to access elements within a component. Here we access the textarea to get its value and then clear the field.React uses refs like jQuery uses selectors. An important difference is you can only easily access refs within your component. This prevents the spaghetti code seen when developers change an element far away from the rest its code.
Tying the App Together
Congratulations! You’ve created two components. One takes user input to create a message and another displays this message. Tie it together with these changes to your main.jsx.import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Message from './components/message';
import MessageCreator from './components/message-creator';
import css from './main.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
author: 'Zach Kuhn',
messages: []
};
}
addMessage(newContent) {
const messages = this.state.messages;
messages.unshift({
id: this.state.messages.length,
author: this.state.author,
content: newContent,
createdAt: new Date()
});
this.setState({ messages: messages });
}
render() {
return (
<div></div>
<messagecreator create="{this.addMessage.bind(this)}"> {</messagecreator>
this.state.messages.map(message => {
return <message author="{message.author}" content="{message.content}" createdat="{message.createdAt}" key="{message.id}">; </message>
})
}
);
}}
ReactDOM.render(<app>, document.querySelector('#react-mount'));</app>
Up top we pull in some CSS to style the page a little nicer. You can find it within the repo: https://github.com/zachary-kuhn/messages-example.The constructor is a feature of ES2015 classes. When we instantiate a class, the constructor is called. Within it we also see our first use of state. The state property maintains a component’s status. When it changes, the component is re-rendered.In our case, the state only changes when we add a new message. We add it to the front of the messages array and then call setState with the new value. Without the call, React is unaware anything has changed and won’t re-render the component.The best practice is to use a key on the components when rendering a collection of elements. The key property helps React keep track of the items within a collection. By doing so, React can properly re-render components when the collection changes.Run the following command and then check out http://localhost:8080 to see the app in action!$ webpack-dev-server --content-base app/
What’s Next
We have a simple app that uses some of the most common parts of React. It’s the 80% needed to create any component. React really is that simple.Upcoming in this series is a look at how we can use a Flux architecture with what we have. This fundamentally changes how we look at state. Although Flux is not part of React, it is often how people structure real world applications.Flux simplifies how applications model state. Within this architecture, we store state in a central place. Components concern themselves with only showing a piece of that state. Whenever a user interacts with a component, the component signals to the store to update. Changes to the state happen within the store itself, which then alerts components that they need to update what they show. It continues onward in a loop.Writing code like this makes it easy to know where certain pieces of code belong. This is one of those practices that helps keep a project manageable no matter how large it grows.There are many libraries that implement the Flux architecture. Among the most popular are Facebook’s own library, named Flux, and Redux.After learning about Flux, we will look at React Native. This is an exciting technology for writing native iOS and Android components with React. We get the benefits of high-performing user interfaces while being able to reuse code. It’s the promise of Cordova finally realized.Thank you for reading! Looking forward to delivering the next part in this series soon.
Learn React Series
- Part 1: Introduction to React.js
- Part 2: ES2015 and JSX; Intermediate components with props and state
- Part 3: Flux architecture and Immutable.js
- Part 4: React Native
Want to learn why I think React has a bright future? Check out our article comparing React against Angular and Ember.