React使用组合而不是继承来处理父子组件,这也是React的特点之一。组件的复合只是React提供的用于定制和特殊化组件的方式之一。React的mixins提供了一种途径,帮助我们定义可以在多组件之间共用的方法。
Mixins will allow you to apply behaviors to multiple React components.
先直观的感受下
1 | var Timer = React.createClass({ |
调用的顺序是:"getInitialState" ``"componentDidMount" ``"tick"
。是一个简单的定时器组件。
当我们用了mixin,不过如果我们有很多的定时器,执行的都是上面的这段代码,难道我们要写很多吗?任何一个语言里面都有复用的方法,任何一个框架也是。连sass,less也都有 :) 。下面是react-mixins的用法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25var IntervalMixin = function(interval){
return {
componentDidMount:function(){
this._interval = setInterval(this.tick,1000)
},
componentWillUnmount:function(){
clearInterval(this._interval)
}
}
}
var Timer = React.createClass({
mixins:[IntervalMixin(1000)],
getInitialState:function(){
return {secondsElapsed:0}
},
tick:function(){
this.setState({secondsElapsed:this.state.secondsElapsed+1})
},
render:function(){
return (
<div>Seconds Elapsed:{this.state.secondsElapsed}</div>
)
}
})
ReactDOM.render(<Timer />,document.getElementById('example'))
这么看来mixins是不是是相当于把一部分功能提取出来公用?更通俗的解释:就是类似于merge,A 有3个方法 compoment
有2个方法,mixin[A]
以后,就有5个方法。并且调用没有任何问题。
几个需要注意的地方
在mixin中写的生命周期相关的回调都会被合并,也就是他们都会执行,而不会互相覆盖掉。比如1
2
3
4
5
6React.createClass({
mixins:[{
getInitialState:function(){return {a:1}}
}],
getInitialState:function(){return {b:2}}
})
这样下来,最后得到的初始state
是{a:1,b:2}
,如果mixin
中的方法和组件类中的方法返回的对象中存在重复的键,React会抛出一个错误来警示。
再比如:你在mixin
中可以定义 componentDidMount
来初始化组件,他不会覆盖掉使用这个mixin
的组件。实际执行的时候,会先执行mixin
的 componentDidMount
,最后执行组件的 componentDidMount
方法。
因为mixin的作用是抽离公共功能,不存在渲染dom的需要,所以它没有render方法。如果你定义了render方法,那么他会和组件的render方法冲突而报错。
同样,mixin
不应该污染state
,所以他也没有 setState
方法。mixin
应该只提供接口(即方法),不应该提供任何属性。mixin
内部的属性最好是通过闭包的形式作为私有变量存在。
1 | var Timer = function() { |
除了生命周期方法可以重复以外,其他的方法都不可以重复,否则会报错。比如有一个Amixin,有一个Bmixin
,并且Amixin
和Bmixin
中都有方法find()
,当他们同时被引入到一个component
时,就会报错。再比如Aminx
里面有,引入它的component
也有find()
方法。那么也会报错。除非他们不是find()
的方法,而是声明周期的方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27var Amixin = {
find: function () { //... }
};
var Bmixin = {
find: function () { //... }
};
//第一种情况
var Component = React.createClass({
mixins: [Amixin,Bmixin],
render: function () {
return (
//...
)
}
});
//或者第二种情况
var Component = React.createClass({
mixins: [Amixin],
find:function(){ //... }
render: function () {
return (
//...
)
}
});
这两种情况都报错。那么如果这个时候我们确实用的是生命周期里面的方法,那么在第一种情况里面,声明周期方法调用顺序又是怎么样的?
Mixins
数组引入的顺序,决定了Mixins
里生命周期方法的执行顺序。并且都在当前组件里面的该方法前面调用。
另一个例子
下面这个例子记录了从2014/1/1到现在的总秒数。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27var IntervalMixin = {
setInterval:function(callback,interval){
var token = setInterval(callback,interrval)
this._intervals.push(token)
return token
},
componentDidMount:function(){
this._intervals = []
},
componentWillUnmount:function(){
this._interval.map(clearInterval)
}
}
var Timer = React.createClass({
mixins:[IntervalMixin],
componentDidMount:function(){
this.setInterval(this.forceUpdate.bind(this),1000)
},
render:function(){
var from = Number(new Date(2014,0,1))
var to = Date.now()
return (
<div>{Math.round(to-from)/1000}</div>
)
}
})
ReactDOM.render(<Timer />,document.getElementById('example'))
总结
还是很好理解的。mixins
大大的解决了代码重复的强大的工具。也减少了我们的阅读量。同时允许我们在不污染组件本身的情况下做一些丑陋的处理。当然也应该取思考,什么时候用mixin比较好。不要盲目的用。