『译』React Mixin 的使用

14

update: Mixin 只适用于 ES5。如果你的项目里面用的是 ES6 ,可以采用高阶组件来实现 Mixin 的功能。

我使用 React.js 构建大型项目已经有一段时间了,我遇到过很多在不同的组件中都要用到相同功能的情况。因此,我花了一个小时左右的时间去了解mixin的用法,然后分享我所学习到的一些东西。

为什么使用 Mixin ?

React回避子类组件,但是我们知道,到处重复地编写同样的代码是不好的。所以为了将同样的功能添加到多个组件当中,你需要将这些通用的功能包装成一个mixin,然后导入到你的模块中。 可以说,相比继承而已,React更喜欢这种组合的方式。嗯,我也喜欢这样。

写一个简单的 Mixin

现在假设我们在写一个app,我们知道在某些情况下我们需要在好几个组件当中设置默认的name属性。
现在,我们不再是像以前一样在每个组件中写多个同样的getDefaultProps方法,我们可以像下面一样定义一个mixin

var DefaultNameMixin = {
    getDefaultProps: function () {
        return {name: "Skippy"};
    }
};

它没什么特殊的,就是一个简单的对象而已。

加入到 React 组件中

为了使用mixin,我们只需要简单得在组件中加入mixins属性,然后把刚才我们写的mixin包裹成一个数组,将它作为该属性的值即可:

var ComponentOne = React.createClass({
    mixins: [DefaultNameMixin],
    render: function() {
    return <h2>Hello {this.props.name}</h2>;
    }
});

React.renderComponent(<ComponentOne />, document.body);

JSFiddle 示例:一个简单的 mixin 例子

重复使用

就像你想象的那样,我们可以在任何其他组件中包含我们的mixin

var ComponentTwo = React.createClass({
    mixins: [DefaultNameMixin],
    render: function () {
        return (
            <div>
                <h4>{this.props.name}</h4>
                <p>Favorite food: {this.props.food}</p>
            </div>
        );
    }
});

JSFiddle 示例:在多个组件中使用同一个 mixin

生命周期方法会被重复调用!

如何你的mixin当中包含生命周期方法,不要焦急,你仍然可以在你的组件中使用这些方法,而且它们都会被调用:

JSFiddle 示例:展示了两个 default props 都会被设置

两个getDefaultProps方法都将被调用,所以我们可以得到默认为Skippyname属性和默认为Pancakesfood属性。任何一个生命周期方法或属性都会被顺利地重复调用,但是下面的情况除外:

  • render:包含多个render方法是不行的。React 会跑出异常:

Uncaught Error: Invariant Violation: ReactCompositeComponentInterface: 
You are attempting to define `render` on your component more than once. 
This conflict may be due to a mixin.
  • displayName:你多次的对它进行设置是没有问题的,但是,最终的结果只以最后一次设置为准。

需要指出的是,mixin是可以包含在其他的mixin中的:

var UselessMixin = {
    componentDidMount: function () {
      console.log("asdas");
    }
};

var LolMixin = {
   mixins: [UselessMixin]
};

var PantsOpinion = React.createClass({
   mixins: [LolMixin],
   render: function () {
       return (<p>I dislike pants</p>);
   }
});

React.renderComponent(<PantsOpinion />, document.body);

程序会在控制台打印出asdas

包含多个 Mixins

我们的mixins要包裹在数组当中,提醒了我们可以在组件中包含多个mixins

var DefaultNameMixin = {
    getDefaultProps: function () {
        return {name: "Lizie"};
    }
};

var DefaultFoodMixin = {
    getDefaultProps: function () {
        return {food: "Pancakes"};
    }
};

var ComponentTwo = React.createClass({
    mixins: [DefaultNameMixin, DefaultFoodMixin],
    render: function () {
        return (
            <div>
                <h4>{this.props.name}</h4>
                <p>Favorite food: {this.props.food}</p>
            </div>
        );
    }
});

注意事项

这里有几件事需要引起我们的注意,当我们使用mixins的时候。 幸运地是,这些看起来并不是什么大问题,下面是我们在实践当中发现的一些问题:

设置相同的 Prop 和 State

如果你尝试在不同的地方定义相同的属性时会出现下面的异常:

Uncaught Error: Invariant Violation: mergeObjectsWithNoDuplicateKeys(): 
Tried to merge two objects with the same key: name

设置相同的方法

在不同的mixin中定义相同的方法,或者mixin和组件中包含了相同的方法时,会抛出异常:

var LogOnMountMixin = {
    componentDidMount: function () {
        console.log("mixin mount method");
        this.logBlah()
    },
    // add a logBlah method here...
    logBlah: function () {
        console.log("blah");
    }
};

var MoreLogOnMountMixin = {
    componentDidMount: function () {
        console.log("another mixin mount method");
    },
    // ... and again here.
    logBlah: function () {
        console.log("something other than blah");
    }
};

异常信息同多次定义rander方法时抛出的异常一样:

Uncaught Error: Invariant Violation: ReactCompositeComponentInterface: 
You are attempting to define `logBlah` on your component more than once. 
This conflict may be due to a mixin.

多个生命周期方法的调用顺序

如果我们的组件和mixin中都包含了相同的生命周期方法的话会怎样呢?

我们的mixin方法首先会被调用,然后再是组件的中方法被调用。

那当我们的组件中包含多个mixin,而这些mixin中又包含相同的生命周期方法时,调用顺序又是如何?

它们会根据mixins中的顺序从左到右的进行调用。

实例代码:

var LogOnMountMixin = {
    componentDidMount: function () {
        console.log("mixin mount method");
    }
};

var MoreLogOnMountMixin = {
    componentDidMount: function () {
        console.log("another mixin mount method");
    }
};
var ComponentOne = React.createClass({
    mixins: [MoreLogOnMountMixin, LogOnMountMixin],
    componentDidMount: function () {
        console.log("component one mount method");
    },
    ...

var ComponentTwo = React.createClass({
    mixins: [LogOnMountMixin, MoreLogOnMountMixin],
    componentDidMount: function () {
        console.log("component two mount method");
    },
    ...

控制台将输出:

another mixin mount method
mixin mount method 
component one mount method

mixin mount method
another mixin mount method 
component two mount method

总结

Mixin 使你React程序变得更为可重用,It's a Good Thing.
我希望这篇文章能够对你有所帮助,如果有任何反馈,很高兴你能够在twitter上@veddermatic

原文链接:http://simblestudios.com/blog...

翻译水平有限,文中带有个人理解,如有不恰当的地方,请在评论中指出,非常感谢!


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

18 条评论
YeatsZhang · 2015-07-27

https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1....

只需要一个mixin的时候使用extends继承一个拥有你mixin方法的react component就可以了 多个mixin可以采用类似decorator的方式对component进行修饰,例如https://github.com/angus-c/es6-react-mixins

+1 回复

YeatsZhang · 2015-07-26

react现在的发展趋势是摆脱mixin的方式,不建议继续使用,可以考虑用extend 或者高阶函数的方式。 不过对于mixin的介绍还是很到位的~

回复

MockingBird 作者 · 2015-07-27

我的React的认识还处于初级阶段。 请问有没有介绍extend、高阶函数的方式的一些资料供我参考参考?

回复

MockingBird 作者 · 2015-07-27

Thx!

回复

灯盏细辛 · 2015-07-31

文章好,评论更好!

回复

xcatliu · 2015-11-30

赞!

回复

onioNN · 2015-12-29

很多地方没写清楚啊

回复

0

这位同学,这篇博文我觉得已经写得很详细了。如果非要揪毛病的话,在“注意事项”里的亮点例子论证不够严密。
1在不同的地方设置相同的 Prop 和 State会出现异常。如下代码会报错:
var GetNameMixin1 = {

getDefaultProps: function () {
    return {name: "Tom"};
}

};
var GetNameMixin2 = {

getDefaultProps: function () {
    return {name: "John"};
}

};
var Component = React.createClass({

mixins: [GetNameMixin1,GetNameMixin2],
render: function () {
    return (
        <div>{this.props.name}</div>
    )
}

});//Uncaught Error: Invariant Violation: mergeObjectsWithNoDuplicateKeys():
Tried to merge two objects with the same key: name.
但是,我认为mixin是不应该污染state和props的,mixin应该只提供接口(即方法),不应该提供任何属性。mixin内部的属性最好是通过闭包的形式作为私有变量存在。
2.设置相同的方法会报错。在React中,除了生命周期方法可以重复以外,其他的方法都不可以重复。如果在mixins来的方法和自身方法中出现多个同名方法,那么在调用该方法的时候会报错,例子如下:
第一种情况:
var AMixin = {

log: function () {
    console.log('AMixin Log');
}

};
var BMixin = {

log: function () {
    console.log('BMixin Log');
}

};
var Component = React.createClass({

mixins: [AMixin,BMixin],
render: function () {
    return (
        <div>Component</div>
    )
}

});//报错
第二种情况:
var Mixin = {

log: function () {
    console.log('Mixin Log');
}

};
var Component = React.createClass({

mixins: [Mixin],
log:function(){
    console.log('Component Log');
},
render: function () {
    return (
        <div>Component</div>
    )
}

});//报错
如果存在别的东西不能理解,可以继续互动,也可以直接参考React官方文档,http://www.css88.com/react/do...(汉化版)。
React新手一只,希望对你有所帮助。

SwaTer · 2017-03-23
Pines_Cheng · 2016-01-27

不错!

回复

Pines_Cheng · 2016-01-27

相当不错

回复

Pines_Cheng · 2016-01-27

·good·

回复

Pines_Cheng · 2016-01-27

good

回复

一罐可乐 · 2016-02-29

非常感谢!

回复

zhr_恒睿 · 2016-07-22

非常感谢!

回复

littlefishzhao · 2016-10-18

谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢谢

回复

吴悠 · 2016-11-02

请问es6中怎么使用mixin ?

回复

0

不支持吧,尝试一下高阶函数

南山老幺儿 · 2017-03-25
0

es6 中可以使用高阶组件代替 mixin ,参考:http://wwsun.github.io/posts/...

MockingBird 作者 · 2017-03-28
载入中...