在HTML,元素(Element)是组成页面的基本元素。而在React中,组成页面的基本元素是组件。React组件本质上是一个JavaScript函数,它接受属性(props)和状态两个参数,并输出render()渲染好的HTML。设计组件时,应该把通用元素(如:按钮、输入框、表单、布局组件等)拆分成接口定义良好的、可复用的、独立的组件,这样不仅可以提高UI的开发效率,同时可以使代码结构更为清晰、减少程序BUG和降低维护成本。
1. propTypes与props的验证
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. getDefaultProps与props的默认值
当组件某个属性为非必须,而又需要默认值是,可以用getDefaultProps来设置。
var ComponentWithDefaultProps = React.createClass({
getDefaultProps: function() {
return {
value: 'default value'
};
}
/* ... */
});
通过getDefaultProps设置后,当上级组件中未传入props时,this.props.value中就会使用默认值。getDefaultProps中设置的值,会在组件生命周期内被缓存,这样我们就可以直接使用props中的值,而不用再写判断相关代码。
3. props的传递
一些常用的组件只是对HTML元素的简单扩展,这时我们想将组件的props传递给下级的HTML元素。JSX的spread语法,提供了一种简单方式来传递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方法。你可以在这个构造函数中设置你自己的状态属性。
另一点不同是,propTypes和defaultProps需要像定义属性一样在构造函数中定义,而不是在类体中定义。
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。
