|
|
|
---
|
|
|
|
id: reusable-components-zh-CN
|
|
|
|
title: 可复用组件
|
|
|
|
permalink: reusable-components-zh-CN.html
|
|
|
|
prev: multiple-components-zh-CN.html
|
|
|
|
next: transferring-props-zh-CN.html
|
|
|
|
---
|
|
|
|
|
|
|
|
设计接口的时候,把通用的设计元素(按钮,表单框,布局组件等)拆成接口良好定义的可复用的组件。这样,下次开发相同界面程序时就可以写更少的代码,也意义着更高的开发效率,更少的 Bug 和更少的程序体积。
|
|
|
|
|
|
|
|
|
|
|
|
## Prop 验证
|
|
|
|
|
|
|
|
随着应用不断变大,保证组件被正确使用变得非常有用。为此我们引入 `propTypes`。`React.PropTypes` 提供很多验证器 (validator) 来验证传入数据的有效性。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。注意为了性能考虑,只在开发环境验证 `propTypes`。下面用例子来说明不同验证器的区别:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
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!');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/* ... */
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 默认 Prop 值
|
|
|
|
|
|
|
|
React 支持以声明式的方式来定义 `props` 的默认值。
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
var ComponentWithDefaultProps = React.createClass({
|
|
|
|
getDefaultProps: function() {
|
|
|
|
return {
|
|
|
|
value: 'default value'
|
|
|
|
};
|
|
|
|
}
|
|
|
|
/* ... */
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
当父级没有传入 props 时,`getDefaultProps()` 可以保证 `this.props.value` 有默认值,注意 `getDefaultProps` 的结果会被 *缓存*。得益于此,你可以直接使用 props,而不必写手动编写一些重复或无意义的代码。
|
|
|
|
|
|
|
|
## 传递 Props:小技巧
|
|
|
|
|
|
|
|
有一些常用的 React 组件只是对 HTML 做简单扩展。通常,你想少写点代码来把传入组件的 props 复制到对应的 HTML 元素上。这时 JSX 的 _spread_ 语法会帮到你:
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
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.PropTypes.element` 可以限定只能有一个子级传入。
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
var MyComponent = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
children: React.PropTypes.element.isRequired
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
{this.props.children} // 有且仅有一个元素,否则会抛异常。
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Mixins
|
|
|
|
|
|
|
|
组件是 React 里复用代码最佳方式,但是有时一些复杂的组件间也需要共用一些功能。有时会被称为 [跨切面关注点](https://en.wikipedia.org/wiki/Cross-cutting_concern)。React 使用 `mixins` 来解决这类问题。
|
|
|
|
|
|
|
|
一个通用的场景是:一个组件需要定期更新。用 `setInterval()` 做很容易,但当不需要它的时候取消定时器来节省内存是非常重要的。React 提供 [生命周期方法](/react/docs/working-with-the-browser.html#component-lifecycle) 来告知组件创建或销毁的时间。下面来做一个简单的 mixin,使用 `setInterval()` 并保证在组件销毁时清理定时器。
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
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')
|
|
|
|
);
|
|
|
|
```
|
|
|
|
|
|
|
|
关于 mixin 值得一提的优点是,如果一个组件使用了多个 mixin,并用有多个 mixin 定义了同样的生命周期方法(如:多个 mixin 都需要在组件销毁时做资源清理操作),所有这些生命周期方法都保证会被执行到。方法执行顺序是:首先按 mixin 引入顺序执行 mixin 里方法,最后执行组件内定义的方法。
|