📜 ⬆️ ⬇️

React on ES6 +

This is a translation of the post by Steven Luscher published on the Babel blog. Steven is working on Facebook on Relay - a JavaScript framework for building applications using React and GraphQL.
This year, during the reorganization of Instagram Web , we enjoyed using a number of features of ES6 + when writing our React components. Let me dwell on those moments when new language features can affect how you write React applications, and make this process easier and more fun than ever.


Classes



Until now, the most noticeable of the visible changes in how we write our React components using ES6 + is that we decided to use the class definition syntax . Instead of using the React.createClass method to define a component, we can define a real ES6 class that extends the React.Component class:
')
class Photo extends React.Component { render() { return <img alt={this.props.caption} src={this.props.src} />; } } 

You will immediately notice a slight difference - you will have access to a more concise syntax for class definition:

 // The ES5 way var Photo = React.createClass({ handleDoubleTap: function(e) { … }, render: function() { … }, }); 

 // The ES6+ way class Photo extends React.Component { handleDoubleTap(e) { … } render() { … } } 

It is worth noting that we discarded two brackets and a terminating semicolon, and for each declared method we omit the colon, the function keyword, and the comma.

All methods of the component life cycle, except one, can be defined, as one would expect, using the new class definition syntax. The class constructor currently acts as the previously used componentWillMount method:

 // The ES5 way var EmbedModal = React.createClass({ componentWillMount: function() { … }, }); 

 // The ES6+ way class EmbedModal extends React.Component { constructor(props) { super(props); // Operations usually carried out in componentWillMount go here } } 


Property Initializers



In the world of ES6 + classes, property types and default values ​​can exist as static properties of this class. These variables, as well as the initial state of the component, can be defined using ES7 property initializers :

 // The ES5 way 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, }, }); 

 // The ES6+ way 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, } } 

ES7 property initializers work inside a class constructor, where this refers to the current instance of the class before it is created. Due to this, the initial state of the component may depend on this.props . It is noteworthy that we no longer have to define the default props values ​​and the initial state of the object in terms of the getter function.


Arrow functions



The React.createClass method is used to perform some additional work on binding to the methods of a component instance, to make sure that within them, the this keyword will refer to the component instance.

 // 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}); }, }); 

Since we are not connected using the React.createClass method, when defining components with the syntax of ES6 + classes, it would seem that we need to manually bind the instance methods to where we want to use them:

 // Manually bind, wherever you need to class PostInfo extends React.Component { constructor(props) { super(props); // Manually bind this method to the component instance... this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this); } handleOptionsButtonClick(e) { // ...to ensure that 'this' refers to the component instance here. this.setState({showOptionsModal: true}); } } 

Fortunately, by combining the two features of ES6 + - arrow functions and property initializers - the failure to bind to a component instance becomes very easy:

 class PostInfo extends React.Component { handleOptionsButtonClick = (e) => { this.setState({showOptionsModal: true}); } } 

The body of the ES6 arrow functions uses the same lexical this as the code that surrounds it. This allows us to achieve the desired result due to the fact that ES7 property initializers are in scope. Take a look under the hood to see why it works.


Dynamic property names and template strings



One of the improvements to object literals includes the ability to assign derived names to properties. Initially, we could do something similar to set some part of the component state:

 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 create objects in which property names are determined by a JavaScript expression at run time. Here we use template strings to determine which property to set in the component state:

 class Form extends React.Component { onChange(inputName, e) { this.setState({ [`${inputName}Value`]: e.target.value, }); } } 


Destructuring and Distributing Attributes



Often when creating components, we could pass most of the properties of the parent component to the child component, but not all of them. In combination with ES6 + destructuring and distribution of JSX attributes , this becomes possible without dancing with a tambourine:

 class AutoloadingPostsGrid extends React.Component { render() { var { className, ...others, // contains all properties of this.props except for className } = this.props; return ( <div className={className}> <PostsGrid {...others} /> <button onClick={this.handleLoadMoreClick}>Load more</button> </div> ); } } 

We can also combine JSX attribute distribution with regular attributes, using a simple priority rule to implement value overrides and set default attribute values. This element will get the override value of the className attribute, even if the className property exists in this.props :

 <div {...this.props} className="override"> … </div> 

The className attribute of this element defaults to "base" if the className property in this.props does not exist to override it:

 <div className="base" {...this.props}> … </div> 


Thanks for reading



I hope that you, like us, will like to use the features of the ES6 + language to write React code. Thanks to my colleagues for their contribution to this article, and special thanks to the Babel team for making the future accessible to all of us, today.

Source: https://habr.com/ru/post/262183/


All Articles