值得注意的是,我们已经删除了两个括号和一个后缀分号,而对于每个声明的方法,我们都省略了一个冒号,一个function关键字和一个逗号。
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() { ... }
}
所有的生命周期方法,但可以定义为使用新的类语法期望的。该课程constructor现在承担以前填写的角色componentWillMount:
// es5
var EmbedModal = React.createClass({
componentWillMount: function() { ... }
})
// es6
class EmbedModal extends React.createClass (
construtor(props) {
super(props);
// Operations usually carried out in componentWillMount go here
}
)
ES7属性初始化器在类的构造函数中运行,其中this指的是正在构建的类的实例,所以初始状态仍然可以依赖于this.props。值得注意的是,我们不再需要根据吸气功能定义道具默认值和初始状态对象。
// es5
var Video = React.createClass({
getDefaultProps: function() {
return {
autoPlay: flase,
maxLoops: 10,
};
},
getInitalState: function() {
return {
loopsRemaining: this.props.maxloops,
};
},
propsTypes: {
autoPlay: React.PropsTypes.bool.isRequired,
maxloops: React.PropsTypes.number.isRequired,
posterFrameSrc: React.PropsTypes.string.isRequired,
videoSrc: React.PropsTypes.string.isRequired
},
});
// es6
class Video extends React.Component {
static defaultProps = {
autoPlay: false,
maxloops: 10
}
static propsTypes = {
autoPlay: React.PropsTypes.bool.isRequired,
maxloops: React.propsTypes.number.isRequired,
posterFrameSrc: React.PropsTypes.string.isRequired,
videoSrc: React.PropsTypes.string.isRequired
}
state = {
loopsRemaining: this.props.maxloops,
}
}
该React.createClass方法用于对组件的实例方法执行一些额外的绑定工作,以确保其中的this关键字将引用该组件的实例。
// 自动绑定React.createClass
var PostInfo = React.createClass({
handleOptionsButtonClick: function(e) {
// 这里,'this' 是指组件实例。
this.setState({showOptionModal: true});
},
});
由于我们React.createClass在使用ES6 +类语法定义组件时不涉及该方法,所以我们需要手动绑定实例方法,无论我们想要这种行为:
// 手动绑定,无论你是否需要
class PostInfo extends React.Component {
construtor(props) {
super(props);
// 手动将此方法绑定到组件实例中…
this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
}
handleOptionsButtonClick(e) {
// …为了确保'this'指的是组件实例。
this.setState({showOptionsModal: true});
}
}
幸运的是,通过组合两个ES6 +功能 - 箭头函数和属性初始值设置 - 选择加入到组件实例的绑定变得轻而易举:
class PostInfo extends React.Component {
handleOptionsButtonClick = (e) => {
this.setState({showOptionsModal: true});
}
}
ES6箭头函数的主体具有与它们周围this的代码相同的词汇,这使得我们获得了期望的结果,因为ES7属性初始化器的作用域的方式。在罩下看,看看为什么这个工作。
其中的增强对象文本包括分配给派生属性名称的能力。我们原本可以做这样的事情来设定一个状态: ```js var From = React.createClass({ onChange: function(inputName, e) { var stateToSet = {}; stateToSet[inputName + 'Value'] = e.targe.value; this.setState(stateToset); }, }); // 现在,我们有能力构建在运行时由JavaScript表达式决定其属性名称的对象。在这里,我们使用模板字符串来确定要设置的属性: class From extends React.Component { onChange(inpuntName, e){ this.setState({
[`$(inputName)value`]: e.targe.value,
});
}
}
## 解构和传播属性
>通常在组成组件时,我们可能希望将大部分父组件的道具传递给子组件,但并不是全部。将ES6 + 解构与JSX 传播属性相结合,无需仪式即可实现:
```js
class AutoloadingPostsGrid extends React.Component {
render() {
const {
className,
...others // 包含所有特性this.props除了类名
} = this.props;
return (
<div className={className}>
<PostsGrid {...others} />
<button onClick={this.handleLoadMoreClick}>Load more</button>
</div>
);
}
}
// 我们可以将JSX传播属性与常规属性结合起来,利用简单的优先级规则来实现覆盖和默认值。该元素将获得className“覆盖”,即使存在一个className财产this.props:
<div {...this.props} className="override">
...
</div>
// This element will regularly have the “base” unless there exists a property in to override it:
<div className="base" {...this.props}>
...
</div>
// 引用
// es5
var React = require("react");
var {
Component,
PropsTypes
} = React; //引用react抽象组件
var ReactNative = require("react-native");
var {
Image,
Text,
} = ReactNative; // 引用具体的React Native组件
// es6 import写法更为标准
import React, {
Component,
PropsTypes,
} from 'react';
import {
imange,
Text,
} from 'react-native'
在ES5里,要导出一个类给别的模块用,一般通过module.exports来导出
// es5
var MyComponent = React.createClass({
// 函数
})
// es6
export default class MyComponent extends Component{
// 函数
}
// 引用 class
// es5
var MyComponent = require(('./MyComponent'));
// es6
import MyComponent from './MyComponent';
es5 在ES5里,通常通过React.createClass来定义一个组件类,像这样:
var Photo = React.createClass({
render: function() {
return (
<Image source={this.props.source} />
)
}
})
在ES6里,我们通过定义一个继承自React.Component的class来定义一个组件类,像这样:
class Photo extends React.Component {
render() {
return (
<Image source={this.props.source} />
);
}
}
给组件定义方法不再用 名字: function()的写法,而是直接用名字(),在方法的最后也不能有逗号了。
// es5
var Photo = React.createClass({
componentWillMount: function() {
// 函数
},
render: function() {
return (
<Image source={this.props.source} />
);
},
});
// es6
class Photo extends React.Component {
componentWillMount() {
// 函数
},
render() {
return (
<Image source={this.props.source} />
);
}
}
在ES5里,属性类型和默认属性分别通过propTypes成员和getDefaultProps方法来实现
// es5
var video = React.createClass({
getDefaultProps: function() {
return {
autoPlay: false,
maxloops: 10,
};
},
PropsTypes: {
autoPlay: React.PropsTypes.bool.isRequired,
maxloops: React.PropsTypes.number.isRequired,
posterFrameSrc: React.PropsTypes.string.isRequired,
videoSrc: React.PropsTypes.string.isRequired,
},
render: function() {
return: function() {
<View />
};
}
})
在ES6里,可以统一使用static成员来实现
//es6
class Video extends React.Component {
static defaultProps = {
autoPlay: flase,
maxloops: 10,
}; // 注意这里有分号
static propsTypes = {
autoPlay: React.PropsTypes.bool.isRequired,
maxloops: React.PropsTypes.number.isRequired,
posterFrameSrc: React.PropsTypes.string.isRequired,
videoSrc: React.PropsTypes.string.isRequired,
}; // 注意这里有分号
render() {
return (
<view />
)
}
}
也有人这么写,虽然不推荐,但读到代码的时候你应当能明白它的意思:
//es6
class Videlo extends React.Component {
render() {
return (
<Video />
);
}
}
Video.defauleftProps = {
autoPlay: false,
maxloops: 10,
};
Video.propsTypes = {
autoPlay: React.PropsTypes.bool.isRequired,
maxloops: React.PropsTypes.number.isRequired,
posterFrameSrc: React.PropsTypes.string.isRequired,
videoSrc: React.PropsTypes.string.isRequired,
}
// 注意: 对React开发者而言,static成员在IE10及之前版本不能被继承,而在IE11和其它浏览器上可以,这有时候会带来一些问题。React Native开发者可以不用担心这个问题。
// es5
var video = React.createClass({
getInitalState: function() {
return {
loopsRemaining: this.props.maxloops,
};
}
})
// ES6下,有两种写法:
class Video extends React.Component {
state = {
loopsRemaining: this.props.maxloops,
}
}
// 不过我们推荐更易理解的在构造函数中初始化(这样你还可以根据需要做一些计算):
// es6
class Video extends React.Component {
construtor(props){
super(props);
this.state = {
loopsRemaining: this.props.maxloops,
}
}
}
很多习惯于ES6的用户反而不理解在ES5下可以这么做:
var PostInfo = React.createClass({
handleOptionsButtonClick: function(e) {
// Here,'this' refers to the component instance
this.setState({showOptionModel: true});
},
render: function() {
return (
<TouchableHighlight onPress={this.handleOptionsButtonClick} >
<Text>{this.props.label}</Text>
</TouchableHighlight>
)
},
});
在ES5下,React.createClass会把所有的方法都bind一遍,这样可以提交到任意的地方作为回调函数,而this不会变化。但官方现在逐步认为这反而是不标准、不易理解的。
在ES6下,你需要通过bind来绑定this引用,或者使用箭头函数(它会绑定当前scope的this引用)来调用
// es6
class PostInfo extends React.Component
{
handleOptionsButtonClick(e){
this.setState({showOptionsModal: true});
}
render() {
return (
<TouchableHighlight
onpress={this.handleOptionsButtonClick.bind(this)}
onpress={e=>this.handleOptionsButtonClick(e)}
>
<Text>{this.props.label}</Text>
</TouchableHighlight>
)
},
}
箭头函数实际上是在这里定义了一个临时的函数,箭头函数的箭头=>之前是一个空括号、单个的参数名、或用括号括起的多个参数名,而箭头之后可以是一个表达式(作为函数的返回值),或者是用花括号括起的函数体(需要自行通过return来返回值,否则返回的是undefined)。
// 箭头函数实例
()=>1
v=>v+1
(a,b)=>a+b
()=>{
alert('foo');
}
e=>{
if (e == 0){
return 0;
}
return 1000/e;
}
需要注意的是,不论是bind还是箭头函数,每次被执行都返回的是一个新的函数引用,因此如果你还需要函数的引用去做一些别的事情(譬如卸载监听器),那么你必须自己保存这个引用
// 错误的做法
class PaussMenu extends React.Component{
componentWillMount() {
AppStateIOS.addEventListener('change', this.onAppPaused.bind(this));
}
componentDidUnmount() {
AppStateIOS.removeEventListener('change', this.onAppPaused.buid(this));
}
onAppPaused(event){
}
}
// 正确的做法
class PaussMenu extends React.Component{
construtor(props) {
super(props);
this._OnAppPaused = this.onAppPaused.bind(this);
}
componentWillMount() {
AppStateIOS.addEventListener('change', this._OnAppPaused);
}
componentDidUnmount() {
AppStateIOS.removeEventListener('change', this_onAppPaused);
}
onAppPaused(event) {
}
}
// 从这个帖子[这个帖子](http://www.tuicool.com/articles/Rj6RFnm)中我们还学习到一种新的做法:
// 正确的做法
class PaussMenu extends React.createClass{
componentWillMount() {
AppStateIOS.addEventListener('change', this.onAppPaused);
}
componentDidUnmount() {
AppStateIOS.removeEventListener('change', this.onAppPaused);
}
onAppPaused = (event) => {
// 把方法直接作为一个arrow function的属性来定义,初始化的时候就绑定好了this的指针
}
}
在ES5下,我们经常使用minxin来为我们的类添加一些新的方法,譬如PureRenderMinxin ``` var PurRenderMixin = require('react-addins-pure-render-minxin'); React.createClass({ mixin: [PurRenderMixin],
render: function() {
return <div className={this.props.className}>foo</div>;
}
})
> 然而现在官方已经不再打算在ES6里继续推行Mixin,他们说:Mixins Are Dead. Long Live Composition。<br/>
尽管如果要继续使用mixin,还是有一些第三方的方案可以用,譬如这个方案<br>
不过官方推荐,对于库编写者而言,应当尽快放弃Mixin的编写方式,文中提到Sebastian Markbåge的一段代码推荐了一种新的编码方式:
//Enhance.js import { Component } from 'React'
extends var Enhance = ComposedComponent => class extends Component {
construtor() {
this.state = {dtaa:null}
}
componentDidUnmount() {
this.setState({ data: 'Hello' });
}
render() {
renturn
// HigherOrderComponent.js import { Enhance } from "./Enhance";
class MyComponent { render() { if (!this.data) return
export defaule Enhance(MyComponent); // Enhance component //用一个“增强函数”,来某个类增加一些方法,并且返回一个新类,这无疑能实现mixin所实现的大部分需求。
## ES6+带来的其它好处
### 解构&属性延展
> 结合使用ES6+的解构和属性延展,我们给孩子传递一批属性更为方便了。这个例子把className以外的所有属性传递给div标签:
class AutoloadingPostsGrid extends React.Component { render() { var { className, ...others, } = this.props; return (
> 下面这种写法,则是传递所有属性的同时,用覆盖新的className值:
> 这个例子则相反,如果属性中没有包含className,则提供默认的值,而如果属性中已经包含了,则使用属性中的值