十七、不使用ES6编写React应用
通常你可以使用一个JavaScript的class功能来定义一个React组件:
class Greeting extends React.Component {
render() {
return <h1>hello {this.props.name}</h1>;
}
}
要是你还没有使用ES6的话,你就得使用React.createClass
来创建一个组件了:
var Greeting = React.createClass({
render: function() {
return <h1>hello {this.props.name}</h1>;
}
});
使用ES6的class
来创建一个组件有点类似于React.createClass
,但是会有一些例外。
定义PropTypes和Props默认值
对于功能性组件
和通过ES6的class
创建的类组件
,propTypes
和defaultProps
都可以定义组件的自身属性:
class Greeting extends React.Component {
// 内部逻辑
}
Greeting.propTypes = {
name: React.PropTypes.string.isRequired
}
Greeting.defaultProps = {
name: 'zhangyatao'
}
对于React.createClass
,你需要在传递的对象上定义一个propTypes
的属性和一个getDefaultProps()
方法。
var Greeting = React.createClass({
propTypes: {
name: React.PropTypes.name.isRequired
}
getDefaultProps: function() {
return {name: 'zhangyatao'}
}
// 内部逻辑
});
设置初始化state
在ES6的class
中,你只需要在构造函数中通过this.state
设置初始化state
。
class Greeting extends React.Component {
constructor(props) {
super(props);
this.state = {name: 'zhangyatao'};
}
// 业务逻辑
}
对于React.createClass
,你需要写一个单独的方法getInitialState()
来返回初始化的state
。
var Greeting = React.createClass({
getInitialState: function() {
return {name: 'zhangyatao'};
}
// 内部逻辑
})
自动绑定this
在通过ES6的class
定义的组件中,内部方法必须与ES6的class
保持相同的语义。这意味着它们不会自动将this
绑定到当前实例。 你必须在构造函数中明确使用.bind(this)
来绑定this
:
class ShowMyName extends React.Component {
constructor(props) {
super(props);
this.name = 'zhangyatao';
this.showName = this.showName.bind(this);
}
showName() {
console.log('我的名字是', this.name);
}
render() {
return (
<button onClick={this.showName}>
打印我的名字
</button>
);
}
}
对于React.createClass
你就不需要给每个内部方法绑定this
。
var ShowName = React.createClass({
name: 'zhangyatao',
showName: function() {
console.log('我的名字是', this.name);
},
render: function() {
return <button onClick={this.showName}>打印我的名字</button>;
}
});
这意味着编写通过ES6
的class
定义的组件,会写一些更多的代码用于处理内部方法(就像一些事件处理函数)中的this
,但是好在大型的应用中上面的那种写法性能略好。
如果代码这么写的话太不吸引你,你大可以在使用Babel时启用那些处于实验阶段(stage-2)
的类属性
语法:
class ShowName extends React.Component {
constructor(props) {
super(props);
this.name = 'zhangyatao';
}
// 使用箭头函数,因为剪头函数会直接绑定this
click = () => {
console.log('我的名字是', this.name)
}
render() {
return <button onClick={this.click}>打印我的名字</button>;
}
}
如果你想安全地摆弄这些东西,你有几个选择:
在构造函数中绑定
this
关键字使用箭头函数,就像例子中的
click = () => {console.log('我的名字是', this.name)}
一直使用
React.createClass
混合(mixins)属性
note:
ES6对mixins
没有任何支持。 因此,在使用ES6的class
编写React组件时,不支持mixins
。
我在代码中使用mixins
时发现了许多问题,因此不建议在新代码中使用。
有时,一些不同的组件需要共享一些常见的功能。 一般称为横切
。 React.createClass
允许你使用一个mixins
属性来实现这个需求。
一个比较常见的用例就是你希望通过一定的时间间隔来更新组件自身。
这很容易使用setInterval()
来实现,但重要的是当你不再需要它的时候如何取消setInterval
来节省内存。
React提供了生命周期函数
,让你知道具体在什么时候去创建或销毁组件。 下面让我们创建一个简单的mixins
,这些方法提供一个简单的setInterval()
,当你的组件被销毁时,它会自动清理setInterval
。
var SetIntervalMixin ={
// 组件将要被装载到DOM中
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
// 组件将要从DOM中卸载
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
}
var TickTock = React.createClass({
// 更新mixins
mixins: [SetIntervalMixin],
// 获取并设置默认state
getInitialState: function() {
return {seconds: 0};
},
// 组件已经被加载到DOM中
componentDidMount: function() {
this.setInterval(this.tick, 1000);
},
tick: function() {
this.setState(function(prevState) {
return {seconds: prevState.seconds + 1};
});
},
render: function() {
return <p>已经运行了{this.state.seconds}秒</p>
}
});
ReactDOM.render(
<TickTock />,
document.getElementById('root')
)
如果一个组件使用多个mixins
,并且一些mixins
定义了相同的生命周期函数(即,当组件被销毁时,一些mixins
想要做一些清理),所有的生命周期函数保证都可以被调用到。 React会列出在mixins中运行的那些在公共mixins上定义的方法,然后在组件上依次调用这些方法。