[譯]在 React.js 中使用 ES6+
在今年對 Instagram Web 進行全新的設計的時候,我喜歡在寫 React 組件的時候,用上一些 ES6+ 的新特性。請允許我列舉這些能夠改變你寫 React 應用方式的新特性。比起以往,這些特性能夠使你擼起碼來更加容易、有趣!
類(Class)
使用 ES6+ 來編寫 React 組件最明顯的變化就是我們定義組件(類)的語法的方式。我們可以用定義一個繼承了React.Component的ES6 類來代替原本使用React.createClass的來創建類的方式:
class Photo extends React.Component {
render() {
return <img alt={this.props.caption} src={this.props.src} />;
}
} 我們可以發現這種寫法使得定義組件的方式變得更加簡潔:
// The ES5 way
var Photo = React.createClass({
handleDoubleTap: function(e) { … },
render: function() { … },
});
// The ES6+ way
class Photo extends React.Component {
handleDoubleTap(e) { … }
render() { … }
} 這樣我們可以少寫一對圓括號、一個分號、每個方法的冒號和function關鍵字。
所有生命周期方法都可以采用這種方式來定義。 但是componentWillMount還可以用constructor來代替:
// 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)
在 ES6+ 類中,屬性類型prop type和默認屬性default prop可以通過類中的static來聲明。同時,組件的初始狀態(initial state)可以通過 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 中在構造函數(constructor)下的屬性初始化操作中的this指向的是類的實例,所以初始狀態(initial state)可以通過this.prop(即傳入的參數)來設定。
箭頭函數(Arrow function)
React.createClass方法在你的組件上做了一些額外的綁定工作,以確保在組件實實例的方法內部,this指向的是組件實例自身。
// 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});
},
}); 由于我們使用 ES6+ 的語法定義類的時候沒有采用React.createClass的方式,所以,這樣看來我們不得不手動來綁定這些方法中this的指向:
// 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});
}
} 幸運的是,通過 ES6+ 的箭頭函數( Arrow functions )和屬性初始化( property initializers )這兩個特性使得把函數的this指向綁定為組件的實例變得非常的簡單:
class PostInfo extends React.Component {
handleOptionsButtonClick = (e) => {
this.setState({showOptionsModal: true});
}
} 函數體內的this對象,綁定定義時所在的對象,而不是使用時所在的對象。而恰好屬性初始化( property initializers )剛好在這個作用域內。
動態屬性名 & 字符串模板
在 ES6+ 中對 對象字面量的擴展 使得我們可以在對象字面量中使用表達式來對屬性命名。如果是在 ES5 中,我們也許只能這樣做:
var Form = React.createClass({
onChange: function(inputName, e) {
var stateToSet = {};
stateToSet[inputName + 'Value'] = e.target.value;
this.setState(stateToSet);
},
}); 但是,在 ES6+ 中,我們不僅可以在對象字面量屬性的定義中使用表達式,還有使用使用 字符串模板 :
class Form extends React.Component {
onChange(inputName, e) {
this.setState({
[`${inputName}Value`]: e.target.value,
});
}
} 析構 & 擴展運算符
我們在編寫組件的過程中,經常遇到要從父組件要把自己的很多屬性多傳給子組件的情況。有了 ES6+ 的 析構 和 擴展運算符 特性,這變得非常的方便:
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>
);
}
} 我們可以把 擴展運算符 屬性和普通的屬性結合起來使用,這樣使得我們可以利用優先級來使用屬性的默認值和屬性的覆蓋。下面這個元素會獲得一個override的類(class),及時this.props中有傳遞className屬性。
<div {...this.props} className="override">
…
</div> 下面這種寫法,可以給元素設定默認的className:
<div className="base" {...this.props}>
…
</div> 最后
我希望你能夠享受 ES6+ 的這些特性給你在編寫 React.js 中帶來的好處。感謝我的同事他們為這篇文章作出的貢獻,還有,特別的感謝 Babel 團隊,使得我們可以隨意的使用這些特性。