ES6 is here and now. So writing React JS components in ES6 will be beneficial considering the new features ES6 brings to us.
Let's look at some of these new features:
- Classes
- Enhanced Object Literals
- Block-scoped binding constructs (let + const)
- Property Initializers
- Arrow Functions
- Template Strings
- Spread Attributes
- Desconstructing Attributes
- Generators
- Data Structures (Map, Set, WeakMap, WeakSet)
- ...and many more
Transpilers
We can use ES6 with transpilers
to convert the source code into ES5, so that most of the browsers can still execute JavaScript source code.
Here are few popular transpilers:
- Traceur
- JSTransform (depreciated)
- Babel
Babel
Babel was created by Sebastian McKenzie at the fall of 2014.
Babel supports JSX, ES6 and ES7. It is widely used including Facebook.
Classes
ES6 classes provide you with a concise way of writing React components:
Instead of using the React.createClass method to define a component, we can define a bonafide ES6 class that extends React.Component
ES5:
var Photo = React.createClass({
handleDoubleTap: function(e) { … },
render: function() { … },
});
ES6:
class Photo extends React.Component {
handleDoubleTap(e) { … }
render() { … }
}
Property Initializers
In ES6 property types and defaults are static properties of its class.
Component's initial state can be defined using ES6.
ES5:
var Video = React.createClass({
getDefaultProps: function() {
return {
autoPlay: false,
maxLoops: 10,
};
},
getInitialState: function() {
return {
loopsRemaining: this.props.maxLoops,
};
},
propTypes: {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
},
});
ES6:
class Video extends React.Component {
static defaultProps = {
autoPlay: false,
maxLoops: 10,
}
static propTypes = {
autoPlay: React.PropTypes.bool.isRequired,
maxLoops: React.PropTypes.number.isRequired,
posterFrameSrc: React.PropTypes.string.isRequired,
videoSrc: React.PropTypes.string.isRequired,
}
state = {
loopsRemaining: this.props.maxLoops,
}
}
Arrow Functions
The React.createClass method used to perform some extra binding work on your component's instance methods to make sure that, inside them, the this keyword would refer to the instance of the component in question.
ES5:
// Autobinding, brought to you by React.createClass
var PostInfo = React.createClass({
handleOptionsButtonClick: function(e) {
// Here, 'this' refers to the component instance.
this.setState({showOptionsModal: true});
},
});
By combining two ES6 features – arrow functions
and property initializers
– opt-in binding to the component instance becomes a easier:
ES6:
class PostInfo extends React.Component {
handleOptionsButtonClick = (e) => {
this.setState({showOptionsModal: true});
}
}
Dynamic property names & template strings
One of the enhancements to object literals include the ability to assign to a derived property name. We might have originally done something like this to set a piece of state:
ES5:
var Form = React.createClass({
onChange: function(inputName, e) {
var stateToSet = {};
stateToSet[inputName + 'Value'] = e.target.value;
this.setState(stateToSet);
},
});
Now, we have the ability to construct objects whose property names are determined by a JavaScript expression at runtime. Here, we use a template string to determine which property to set on state:
ES6:
class Form extends React.Component {
onChange(inputName, e) {
this.setState({
[`${inputName}Value`]: e.target.value,
});
}
}
Destructuring & spread attributes
Often when composing components, we might want to pass down most of a parent component's props to a child component, but not all of them. In combining ES6+ destructuring
with JSX spread attributes
, this becomes easier:
ES6:
class AutoloadingPostsGrid extends React.Component {
render() {
var {
className,
// contains all properties of this.props except for className
...others,
} = this.props;
return (
<div className={className}>
<PostsGrid {...others} />
<button onClick={this.handleLoadMoreClick}>Load more</button>
</div>
);
}
}
We can combine JSX spread attributes with regular attributes too, taking advantage of a simple precedence rule to implement overrides and defaults. This element will acquire the className “override” even if there exists a className property in this.props:
<div {...this.props} className="override">
…
</div>
This element will regularly have the className
“base” unless there exists a className
property in this.props to override it:
<div className="base" {...this.props}>
…
</div>