• 可复用组件
    • Prop 验证
      • Single Child
    • 默认 Prop 值
    • 传递 Props:捷径
    • Mixins
    • ES6 Classes
      • 无自动绑定
      • 没有 Mixins
    • 无状态函数

    可复用组件

    设计接口的时候,把通用的设计元素(按钮,表单框,布局组件等)拆成接口良好定义的可复用的组件。这样,下次开发相同界面程序时就可以写更少的代码,也意义着更高的开发效率,更少的 Bug 和更少的程序体积。

    Prop 验证

    随着应用不断变大,保证组件被正确使用变得非常有用。为此我们引入 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 元素或包含这些类型的数组(or fragment) 。
    13. optionalNode: React.PropTypes.node,
    14. // React 元素
    15. optionalElement: React.PropTypes.element,
    16. // 你同样可以断言一个 prop 是一个类的实例。
    17. // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
    18. optionalMessage: React.PropTypes.instanceOf(Message),
    19. // 你可以用 enum 的方式
    20. // 确保你的 prop 被限定为指定值。
    21. optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
    22. // 指定的多个对象类型中的一个
    23. optionalUnion: React.PropTypes.oneOfType([
    24. React.PropTypes.string,
    25. React.PropTypes.number,
    26. React.PropTypes.instanceOf(Message)
    27. ]),
    28. // 指定类型组成的数组
    29. optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
    30. // 指定类型的属性构成的对象
    31. optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
    32. // 特定形状参数的对象
    33. optionalObjectWithShape: React.PropTypes.shape({
    34. color: React.PropTypes.string,
    35. fontSize: React.PropTypes.number
    36. }),
    37. // 你可以在任意东西后面加上 `isRequired`
    38. // 来确保 如果 prop 没有提供 就会显示一个警告。
    39. requiredFunc: React.PropTypes.func.isRequired,
    40. // 不可空的任意类型
    41. requiredAny: React.PropTypes.any.isRequired,
    42. // 你可以自定义一个验证器。如果验证失败需要返回一个 Error 对象。
    43. // 不要直接使用 `console.warn` 或抛异常,
    44. // 因为这在 `oneOfType` 里不起作用。
    45. customProp: function(props, propName, componentName) {
    46. if (!/matchme/.test(props[propName])) {
    47. return new Error('Validation failed!');
    48. }
    49. }
    50. },
    51. /* ... */
    52. });

    Single Child

    React.PropTypes.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. });

    默认 Prop 值

    React 支持以声明式的方式来定义 props 的默认值。

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

    当父级没有传入 props 时,getDefaultProps() 可以保证 this.props.value 有默认值,注意 getDefaultProps 的结果会被 缓存。得益于此,你可以直接使用 props,而不必写手动编写一些重复或无意义的代码。

    传递 Props:捷径

    有一些常用的 React 组件只是对 HTML 做简单扩展。通常,你想 复制任何传进你的组件的HTML属性 到底层的HTML元素上。为了减少输入,你可以用 JSX spread 语法来完成:

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

    Mixins

    组件是 React 里复用代码最佳方式,但是有时一些复杂的组件间也需要共用一些功能。有时会被称为 跨切面关注点。React 使用 mixins 来解决这类问题。

    一个通用的场景是:一个组件需要定期更新。用 setInterval() 做很容易,但当不需要它的时候取消定时器来节省内存是非常重要的。React 提供 生命周期方法 来告知组件创建或销毁的时间。下面来做一个简单的 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.forEach(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. ReactDOM.render(
    32. <TickTock />,
    33. document.getElementById('example')
    34. );

    关于 mixin 值得一提的优点是,如果一个组件使用了多个 mixin,并用有多个 mixin 定义了同样的生命周期方法(如:多个 mixin 都需要在组件销毁时做资源清理操作),所有这些生命周期方法都保证会被执行到。方法执行顺序是:首先按 mixin 引入顺序执行 mixin 里方法,最后执行组件内定义的方法。

    ES6 Classes

    你也可以以一个简单的JavaScript 类来定义你的React classes。使用ES6 class的例子:

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

    API近似于 React.createClass 除了 getInitialState。 你应该在构造函数里设置你的state,而不是提供一个单独的 getInitialState 方法。

    另一个不同是 propTypesdefaultProps 在构造函数而不是class body里被定义为属性。

    1. export class Counter extends React.Component {
    2. constructor(props) {
    3. super(props);
    4. this.state = {count: props.initialCount};
    5. }
    6. tick() {
    7. this.setState({count: this.state.count + 1});
    8. }
    9. render() {
    10. return (
    11. <div onClick={this.tick.bind(this)}>
    12. Clicks: {this.state.count}
    13. </div>
    14. );
    15. }
    16. }
    17. Counter.propTypes = { initialCount: React.PropTypes.number };
    18. Counter.defaultProps = { initialCount: 0 };

    无自动绑定

    方法遵循正式的ES6 class的语义,意味着它们不会自动绑定this到实例上。你必须显示的使用.bind(this) or 箭头函数 =>.

    没有 Mixins

    不幸的是ES6的发布没有任何mixin的支持。因此,当你在ES6 classes下使用React时不支持mixins。作为替代,我们正在努力使它更容易支持这些用例不依靠mixins。

    无状态函数

    你也可以用 JavaScript 函数来定义你的 React 类。例如使用无状态函数语法:

    1. function HelloMessage(props) {
    2. return <div>Hello {props.name}</div>;
    3. }
    4. ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);

    或者使用新的ES6箭头函数:

    1. const HelloMessage = (props) => <div>Hello {props.name}</div>;
    2. ReactDOM.render(<HelloMessage name="Sebastian" />, mountNode);

    这个简化的组件API旨在用于那些纯函数态的组件 。这些组件必须没有保持任何内部状态,没有备份实例,也没有组件生命周期方法。他们纯粹的函数式的转化他们的输入,没有引用。
    然而,你仍然可以以设置为函数的properties的方式来指定 .propTypes.defaultProps,就像你在ES6类里设置他们那样。

    注意:

    因为无状态函数没有备份实例,你不能附加一个引用到一个无状态函数组件。 通常这不是问题,因为无状态函数不提供一个命令式的API。没有命令式的API,你就没有任何需要实例来做的事。然而,如果用户想查找无状态函数组件的DOM节点,他们必须把这个组件包装在一个有状态组件里(比如,ES6 类组件) 并且连接一个引用到有状态的包装组件。

    在理想世界里,你的大多数组件都应该是无状态函数式的,因为这些无状态组件可以在React核心里经过一个快速的代码路径。 如果可能,这是推荐的模式。