React定义可复用的组件


在HTML,元素(Element)是组成页面的基本元素。而在React中,组成页面的基本元素是组件。React组件本质上是一个JavaScript函数,它接受属性(props)和状态两个参数,并输出render()渲染好的HTML。设计组件时,应该把通用元素(如:按钮、输入框、表单、布局组件等)拆分成接口定义良好的、可复用的、独立的组件,这样不仅可以提高UI的开发效率,同时可以使代码结构更为清晰、减少程序BUG和降低维护成本。


  1. propTypesprops的验证
  2. getDefaultPropsprops的默认值
  3. props的传递
  4. Mixins与组件功能共享
  5. ES6 Class在组件中的应用


1. propTypesprops的验证

propTypes是定义组件时的参数对象,该对象中包含了一系列验证props的方法。

对组件传入数据进行验证非常有必要,随着应用的规模不断变大,有效性验证可以保证组件被正确使用。React.PropTypes中提供了提供了很多的验证器,通过这些验证器可以保证传入数据的合法性。在props传入无效数据时,React会向控制台打印一个console.warn日志。

注意:验证器会对组件性能带来一定影响,建议只在开发环境使用propTypes验证。

下面是React.PropTypes中一些不同的验证器:

React.createClass({
  propTypes: {
    // 我们可以像下面这样指定 prop 为JS 原始类型
    // 这些属性值都是可选
    optionalArray: React.PropTypes.array,
    optionalBool: React.PropTypes.bool,
    optionalFunc: React.PropTypes.func,
    optionalNumber: React.PropTypes.number,
    optionalObject: React.PropTypes.object,
    optionalString: React.PropTypes.string,

    // React.PropTypes中提供了所有的可渲染对象的验证
    // 这些对象包括:数字、字符串、元素、数组等

    // 节点判断
    optionalNode: React.PropTypes.node,

    // React元素判断
    optionalElement: React.PropTypes.element,

    // prop 可以定义为一个类的实例
    // 判断为类实例使用JS的instanceOf 操作符判断
    optionalMessage: React.PropTypes.instanceOf(Message),

    // 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),

    // shape 指定传入对象属性名及值类型
    optionalObjectWithShape: React.PropTypes.shape({
      color: React.PropTypes.string,
      fontSize: React.PropTypes.number
    }),

    // 所有类型都可以增加 'isRequired' 来限制prop不可为空
    requiredFunc: React.PropTypes.func.isRequired,

    // 可以同时使用'any'指定任何不可为空的任意类型
    requiredAny: React.PropTypes.any.isRequired,

    // 'element' 可以用于限定单个子组件
    children: React.PropTypes.element.isRequired

    // propTypes 支持使用自定义的验证器。
    // 自定义验证,失败时要返回一个 Error 对象,不能直接使用`console.warn`
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    }
  },
  /* ... */
});

示例:使用React.PropTypes.element指定组中包含且只能包含一个子组件:

var MyComponent = React.createClass({
  propTypes: {
    children: React.PropTypes.element.isRequired
  },

  render: function() {
    return (
      <div>
        {this.props.children} // 组件中必须包含一个子组件,否则会抛出异常
      </div>
    );
  }
});


2. getDefaultPropsprops的默认值

当组件某个属性为非必须,而又需要默认值是,可以用getDefaultProps来设置。

var ComponentWithDefaultProps = React.createClass({
  getDefaultProps: function() {
    return {
      value: 'default value'
    };
  }
  /* ... */
});

通过getDefaultProps设置后,当上级组件中未传入props时,this.props.value中就会使用默认值。getDefaultProps中设置的值,会在组件生命周期内被缓存,这样我们就可以直接使用props中的值,而不用再写判断相关代码。


3. props的传递

一些常用的组件只是对HTML元素的简单扩展,这时我们想将组件的props传递给下级的HTML元素。JSXspread语法,提供了一种简单方式来传递props

var CheckLink = React.createClass({
  render: function() {
    // 这样会把 CheckList 所有的 props 复制到 <a>
    return <a {...this.props}>{'√ '}{this.props.children}</a>;
  }
});

ReactDOM.render(
  <CheckLink href="/checked.html">
    Click here!
  </CheckLink>,
  document.getElementById('example')
);

以上代码输出如下:

<a href="/checked.html" data-reactid=".0">
  <span data-reactid=".0.0">√ </span><span data-reactid=".0.1">Click here!</span>
</a>


4. Mixins与组件功能共享

组件是React中代码共享的最佳方式,有时不同组件间会有一些共用的功能,React 中提供了mixins来解决这类问题。

当一个组件中使用了多个mixin,并将多个mixin定义到了同样的生命周期方法,所有这些定义到生命周期中的方法都会被执行到。方法执行顺序是,首先按mixin的引入顺序执行mixin里方法,最后执行组件内定义的方法。

Mixins使用示例请参考:跨组件共享


5. ES6 Class在组件中的应用

也可以像定义JavaScript类一样定义React类,如:使用ES6 class语法:

class HelloMessage extends React.Component {
  render() {
    return <div>Hello {this.props.name}</div>;
  }
}
ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);

这个API类似于React.createClass创建组件时使用的getInitialState方法。相当于提供了一个单独的getInitialState方法。你可以在这个构造函数中设置你自己的状态属性。

另一点不同是,propTypesdefaultProps需要像定义属性一样在构造函数中定义,而不是在类体中定义。

export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {count: props.initialCount};
  }
  tick() {
    this.setState({count: this.state.count + 1});
  }
  render() {
    return (
      <div onClick={this.tick.bind(this)}>
        Clicks: {this.state.count}
      </div>
    );
  }
}
Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };

无自动绑定,方法的定义也要遵循ES6类的规则,这样就不会自动绑定this,需要明确的使用.bind(this)=>

无Mixins,ES6没有任何对mixin的支持。所在,在使用ES6类定义方式时,也不支持mixin。