@brizer
2016-01-30T12:48:49.000000Z
字数 3541
阅读 1636
本文主要学习了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], // 引用 mixin
getInitialState: 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
方法则是在移除的时候立即调用。