React-mixins在多个组件中复用代码

    React使用组合而不是继承来处理父子组件,这也是React的特点之一。组件的复合只是React提供的用于定制和特殊化组件的方式之一。React的mixins提供了一种途径,帮助我们定义可以在多组件之间共用的方法。

    Mixins will allow you to apply behaviors to multiple React components.


    先直观的感受下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    var Timer = React.createClass({
    getInitialState:function(){
    return {secondsElapsed:0}
    },
    tick:function(){
    this.setState({secondsElapsed:this.state.secondsElapsed+1})
    },
    componentDidMount:function(){
    this.interval = setInterval(this.tick,1000)
    },
    componentWillUnmount:function(){
    clearInterval(this.interval)
    },
    render:function(){
    return (
    <div>Seconds Elapsed:{this.state.secondsElapsed}</div>
    )
    }
    })
    ReactDOM.render(<Timer />,document.getElementById('example'))

    调用的顺序是:"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
    25
    var 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
    6
    React.createClass({
    mixins:[{
    getInitialState:function(){return {a:1}}
    }],
    getInitialState:function(){return {b:2}}
    })

    这样下来,最后得到的初始state{a:1,b:2},如果mixin中的方法和组件类中的方法返回的对象中存在重复的键,React会抛出一个错误来警示。

    再比如:你在mixin中可以定义 componentDidMount来初始化组件,他不会覆盖掉使用这个mixin的组件。实际执行的时候,会先执行mixincomponentDidMount,最后执行组件的 componentDidMount方法。

    因为mixin的作用是抽离公共功能,不存在渲染dom的需要,所以它没有render方法。如果你定义了render方法,那么他会和组件的render方法冲突而报错。

    同样,mixin不应该污染state,所以他也没有 setState 方法。mixin应该只提供接口(即方法),不应该提供任何属性。mixin内部的属性最好是通过闭包的形式作为私有变量存在。

    1
    2
    3
    4
    var Timer = function() {
    var test = 1;//私有属性
    return {test + 1}
    }

    除了生命周期方法可以重复以外,其他的方法都不可以重复,否则会报错。比如有一个Amixin,有一个Bmixin,并且AmixinBmixin中都有方法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
    27
    var 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
    27
    var 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比较好。不要盲目的用。