[关闭]
@brizer 2016-01-30T12:48:49.000000Z 字数 3541 阅读 1605

React之可复用组件


前言

本文主要学习了React中组件复用时需要注意的props问题和mixin问题。


在设计接口时,我们一般把通用的设计元素比如按钮、表单框、布局组件等拆成接口良好定义的可复用组件。这样可以提高开发的效率和质量。


propTypes

随着应用不断变大,保证组件被正确使用变得非常有用。为此我们引入 propTypesReact.PropTypes 提供很多验证器 (validator) 来验证传入数据的有效性。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。注意为了性能考虑,只在开发环境验证 propTypes。下面用例子来说明不同验证器的区别:

  1. React.createClass({
  2. propTypes: {
  3. // 可以声明 prop 为指定的 JS 基本类型。默认
  4. // 情况下,这些 prop 都是可传可不传的。
  5. optionalArray: React.PropTypes.array,
  6. optionalBool: React.PropTypes.bool,
  7. optionalFunc: React.PropTypes.func,
  8. optionalNumber: React.PropTypes.number,
  9. optionalObject: React.PropTypes.object,
  10. optionalString: React.PropTypes.string,
  11. // 所有可以被渲染的对象:数字,
  12. // 字符串,DOM 元素或包含这些类型的数组。
  13. optionalNode: React.PropTypes.node,
  14. // React 元素
  15. optionalElement: React.PropTypes.element,
  16. // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
  17. optionalMessage: React.PropTypes.instanceOf(Message),
  18. // 用 enum 来限制 prop 只接受指定的值。
  19. optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
  20. // 指定的多个对象类型中的一个
  21. optionalUnion: React.PropTypes.oneOfType([
  22. React.PropTypes.string,
  23. React.PropTypes.number,
  24. React.PropTypes.instanceOf(Message)
  25. ]),
  26. // 指定类型组成的数组
  27. optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
  28. // 指定类型的属性构成的对象
  29. optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
  30. // 特定形状参数的对象
  31. optionalObjectWithShape: React.PropTypes.shape({
  32. color: React.PropTypes.string,
  33. fontSize: React.PropTypes.number
  34. }),
  35. // 以后任意类型加上 `isRequired` 来使 prop 不可空。
  36. requiredFunc: React.PropTypes.func.isRequired,
  37. // 不可空的任意类型
  38. requiredAny: React.PropTypes.any.isRequired,
  39. // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接
  40. // 使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
  41. customProp: function(props, propName, componentName) {
  42. if (!/matchme/.test(props[propName])) {
  43. return new Error('Validation failed!');
  44. }
  45. }
  46. },
  47. /* ... */
  48. });

props传值

如果父级没有传入props时,我们可以通过getDefaultProps设置props的默认值,该结果会被缓存。

  1. var ComponentWithDefaultProps = React.createClass({
  2. getDefaultProps: function() {
  3. return {
  4. value: 'default value'
  5. };
  6. }
  7. /* ... */
  8. });

如果我想把传入组件的props复制到对应的HTML元素上,可以用到前面学习JSX语法中提到的...语法:

  1. var CheckLink = React.createClass({
  2. render: function() {
  3. // 这样会把 CheckList 所有的 props 复制到 <a>
  4. return <a {...this.props}>{'√ '}{this.props.children}</a>;
  5. }
  6. });
  7. React.render(
  8. <CheckLink href="/checked.html">
  9. Click here!
  10. </CheckLink>,
  11. document.getElementById('example')
  12. );

我们可以通过React.PropTyps.element来限定只能有一个子级传入。

  1. var MyComponent = React.createClass({
  2. propTypes: {
  3. children: React.PropTypes.element.isRequired
  4. },
  5. render: function() {
  6. return (
  7. <div>
  8. {this.props.children} // 有且仅有一个元素,否则会抛异常。
  9. </div>
  10. );
  11. }
  12. });

Mixins

React使用组件而不是继承来处理父子组件。所以我们无法通过继承父类来实现公共功能,所以React提供了Mixins的机制。

Mixins可以理解为将Mixins对象上的方法都混合到另一个组件上,和jquery中的$.extend类似。

不过,react对mixin做了一些特殊处理。

在mixin中写的生命周期相关的回调都会被合并,也就是说即使定义了多个mixin,他们都会按照顺序执行,而不会互相覆盖掉。
比如 你在mixin中可以定义 componentDidMount 来初始化组件,他不会覆盖掉使用这个mixin的组件。实际执行的时候,会先执行 mixincomponentDidMount最后执行组件的 componentDidMount 方法。

需要注意的是,因为mixin的作用是抽离公共功能,不存在渲染dom的需要,所以它没有render方法。如果你定义了render方法,那么他会和组件的render方法冲突而报错。
同样,mixin不应该污染state,所以他也没有 setState 方法。mixin应该只提供接口(即方法),不应该提供任何属性。

下面来看看一个简单的mixin,使用 setInterval() 并保证在组件销毁时清理定时器:

  1. var SetIntervalMixin = {
  2. componentWillMount: function() {
  3. this.intervals = [];
  4. },
  5. setInterval: function() {
  6. this.intervals.push(setInterval.apply(null, arguments));
  7. },
  8. componentWillUnmount: function() {
  9. this.intervals.map(clearInterval);
  10. }
  11. };
  12. var TickTock = React.createClass({
  13. mixins: [SetIntervalMixin], // 引用 mixin
  14. getInitialState: function() {
  15. return {seconds: 0};
  16. },
  17. componentDidMount: function() {
  18. this.setInterval(this.tick, 1000); // 调用 mixin 的方法
  19. },
  20. tick: function() {
  21. this.setState({seconds: this.state.seconds + 1});
  22. },
  23. render: function() {
  24. return (
  25. <p>
  26. React has been running for {this.state.seconds} seconds.
  27. </p>
  28. );
  29. }
  30. });
  31. React.render(
  32. <TickTock />,
  33. document.getElementById('example')
  34. );

其中的componentWillMountcomponentDidMount方法分别在初始化渲染之前和之后调用,而componentWillUnmount方法则是在移除的时候立即调用。


添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注