@brizer
2016-01-30T04:48:49.000000Z
字数 3541
阅读 1765
本文主要学习了React中组件复用时需要注意的props问题和mixin问题。
在设计接口时,我们一般把通用的设计元素比如按钮、表单框、布局组件等拆成接口良好定义的可复用组件。这样可以提高开发的效率和质量。
随着应用不断变大,保证组件被正确使用变得非常有用。为此我们引入 propTypes。React.PropTypes 提供很多验证器 (validator) 来验证传入数据的有效性。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。注意为了性能考虑,只在开发环境验证 propTypes。下面用例子来说明不同验证器的区别:
React.createClass({propTypes: {// 可以声明 prop 为指定的 JS 基本类型。默认// 情况下,这些 prop 都是可传可不传的。optionalArray: React.PropTypes.array,optionalBool: React.PropTypes.bool,optionalFunc: React.PropTypes.func,optionalNumber: React.PropTypes.number,optionalObject: React.PropTypes.object,optionalString: React.PropTypes.string,// 所有可以被渲染的对象:数字,// 字符串,DOM 元素或包含这些类型的数组。optionalNode: React.PropTypes.node,// React 元素optionalElement: React.PropTypes.element,// 用 JS 的 instanceof 操作符声明 prop 为类的实例。optionalMessage: React.PropTypes.instanceOf(Message),// 用 enum 来限制 prop 只接受指定的值。optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),// 指定的多个对象类型中的一个optionalUnion: React.PropTypes.oneOfType([React.PropTypes.string,React.PropTypes.number,React.PropTypes.instanceOf(Message)]),// 指定类型组成的数组optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),// 指定类型的属性构成的对象optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),// 特定形状参数的对象optionalObjectWithShape: React.PropTypes.shape({color: React.PropTypes.string,fontSize: React.PropTypes.number}),// 以后任意类型加上 `isRequired` 来使 prop 不可空。requiredFunc: React.PropTypes.func.isRequired,// 不可空的任意类型requiredAny: React.PropTypes.any.isRequired,// 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接// 使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。customProp: function(props, propName, componentName) {if (!/matchme/.test(props[propName])) {return new Error('Validation failed!');}}},/* ... */});
如果父级没有传入props时,我们可以通过getDefaultProps设置props的默认值,该结果会被缓存。
var ComponentWithDefaultProps = React.createClass({getDefaultProps: function() {return {value: 'default value'};}/* ... */});
如果我想把传入组件的props复制到对应的HTML元素上,可以用到前面学习JSX语法中提到的...语法:
var CheckLink = React.createClass({render: function() {// 这样会把 CheckList 所有的 props 复制到 <a>return <a {...this.props}>{'√ '}{this.props.children}</a>;}});React.render(<CheckLink href="/checked.html">Click here!</CheckLink>,document.getElementById('example'));
我们可以通过React.PropTyps.element来限定只能有一个子级传入。
var MyComponent = React.createClass({propTypes: {children: React.PropTypes.element.isRequired},render: function() {return (<div>{this.props.children} // 有且仅有一个元素,否则会抛异常。</div>);}});
React使用组件而不是继承来处理父子组件。所以我们无法通过继承父类来实现公共功能,所以React提供了Mixins的机制。
Mixins可以理解为将Mixins对象上的方法都混合到另一个组件上,和jquery中的$.extend类似。
不过,react对mixin做了一些特殊处理。
在mixin中写的生命周期相关的回调都会被合并,也就是说即使定义了多个mixin,他们都会按照顺序执行,而不会互相覆盖掉。
比如 你在mixin中可以定义 componentDidMount 来初始化组件,他不会覆盖掉使用这个mixin的组件。实际执行的时候,会先执行 mixin 的 componentDidMount ,最后执行组件的 componentDidMount 方法。
需要注意的是,因为mixin的作用是抽离公共功能,不存在渲染dom的需要,所以它没有render方法。如果你定义了render方法,那么他会和组件的render方法冲突而报错。
同样,mixin不应该污染state,所以他也没有 setState 方法。mixin应该只提供接口(即方法),不应该提供任何属性。
下面来看看一个简单的mixin,使用 setInterval() 并保证在组件销毁时清理定时器:
var SetIntervalMixin = {componentWillMount: function() {this.intervals = [];},setInterval: function() {this.intervals.push(setInterval.apply(null, arguments));},componentWillUnmount: function() {this.intervals.map(clearInterval);}};var TickTock = React.createClass({mixins: [SetIntervalMixin], // 引用 mixingetInitialState: function() {return {seconds: 0};},componentDidMount: function() {this.setInterval(this.tick, 1000); // 调用 mixin 的方法},tick: function() {this.setState({seconds: this.state.seconds + 1});},render: function() {return (<p>React has been running for {this.state.seconds} seconds.</p>);}});React.render(<TickTock />,document.getElementById('example'));
其中的componentWillMount和componentDidMount方法分别在初始化渲染之前和之后调用,而componentWillUnmount方法则是在移除的时候立即调用。