创建 React 组件三种“姿势”

2,736 阅读4分钟

React 世界中,组成一个页面的最小单元为一个个组件,很显然如何合理的创建它们是个非常关键的点。下面介绍一下我对三种创建组件方式的理解与总结。

从简单开始:函数式组件

了解过 React 的人都知道,一个 Component 数据源有两个:

  1. 来自外部的属性(props)
  2. 来自内部的状态(State)

当函数式的创建一个组件之后,显然它已经注定了没法再拥有自己的 State 了,只能 “无脑” 的去获取属性内容并展示,因为函数式组件其实是一个只实现了 render 函数的组件。

所以把这种组件称之为 “小傻瓜组件” 非常的形象

一个 “傻瓜函数式组件” 的例子:

// 这一句导入必须添加, JSX 语境依赖于此
import React from 'react';

export default ({order="没人下达命令,我就啥也不干"}) => {
    return (
        <div>
            我是“傻瓜组件”,我完全服从命令:{order}.
        </div>
    );
};

这个简单的组件只接受一个 order 的 ,并且为这个属性设置了一个默认值。

  1. 当使用这个组件的地方为它设置了 order 属性,就会打印外部设置的order属性值;
  2. 如果没有设置,就会打印这个默认值(ES6 语法允许给定义的函数参数设置一个默认值)

这里使用了 ES6 箭头语法,快速的创建了一个无状态的函数式组件并 exoprt 出去供外界使用。

复杂点的:继承 React Component 相关类实现

React 版本16以后,官方已经不推荐使用 createclass 的方式创建组件了,同时也取消了以前 Mixin 那一套侵入式复用代码的做法,转而推荐大家使用高阶函数的方式来实现对代码的复用

继承的方式创建一个组件可以继承两个父类:

  1. React.Component
  2. React.PureComponent

PureComponent 和 Component 除了在 shouldComponentUpdate 方法的实现逻辑上不一样,其他表现都一样,PureComponent使用了props和state的浅比较。

一般情况下我们选择继承 React.PureComponent 可以优化我们的组件性能,不过特殊时候你也可以选择继承 React.Component,然后自己去实现 shouldComponentUpdate 方法的逻辑部分。

相比“傻瓜式”组件,继承方式获得的组件更加 “聪明” 了:

1.它可以有自己的属性 2.它可以有自己的状态 3.它可以有自己的生命周期逻辑

一个简单的 “聪明的” 继承式的组件例子:

import React from "react";
import PropTypes from "prop-types";

export default class ComponentOne extends React.PureComponent {
  static defaultProps = {
    propOne: "默认属性"
  };

  static propTypes = {
    propOne: PropTypes.string.isRequired
  };

  constructor(props) {
    super(props);
    console.log("构造函数");
    this.clickToAdd = this.clickToAdd.bind(this);
    this.state = {
      count: 0
    };
  }

  clickToAdd() {
    this.setState({
      count: this.state.count + 1
    });
  }

  componentDidMount() {
    console.log("界面装载完成");
  }

  render() {
    return (
      <div>
          <div>
          我是由继承 React.PureComponent 创建的组件。并且我有属性:{this.props.propOne}
          </div>
          <div>
              当前计数:{this.state.count}
          </div>
          <div>
              <button onClick={this.clickToAdd}>点击计数增加</button>
          </div>
      </div>
    );
  }
}

需要注意的一点,使用继承的方式创建的组件,它的成员函数都不会自动绑定 this,推荐在构造函数 constructor 中进行手动 bind。

上面代码中的组件有自己的状态,有自己的属性,也自己实现了生命周期函数中的 componentDidMount,在里面简单打印了一句 log。

更厉害的:高阶组件 HOC(Higher Order Component)

高阶组件,我的理解就是一个能接受组件为参数,并能返回一个新组件的函数。高阶组件有下面两种实现方式:

1. 代理模式(组合)

先看示例代码:

import React from 'react';

const agent = (WrappedComponent, newProps) => {
    return class WrappingComponent extends React.PureComponent{
        render(){
            return <WrappedComponent {...this.props} {...newProps}/>
        }
    }
}

export default agent;

WrappingComponent 和 WrappedComponent 都分别走一遍自己的生命周期,代理模式下的高阶函数是两个组件的一个组合。

可以用此来操纵传入的 props,达到复用代码的目的。

2. 继承模式(继承)

先看示例代码:

import React from 'react';

const OnlyForLoginComponent = (WrappedComponent) => {
    return class WrappingComponent extends WrappedComponent{
        render(){
            if (this.props.logined) {
                return super.render();
            } else {
                return (
                    <div>
                        请先登录。
                    </div>
                );
            }
        }
    }
}

export default OnlyForLoginComponent;

可以看到这里通过继承的方式实现了一个新的组件并返回。采用继承的方式实现的高阶组件,是一种合二为一的方式,因此我们可以操纵 WrappedComponent 的生命周期,当然也可以操纵它的属性。

比如上面的代码展示的就是,使用高阶组件生成了一个登录状态判断的状态组件,如果登录了就继续渲染 super(也就是 WrappedComponent),否则就提示“请先登录”。

熟悉 React-Redux 库的同学可以探索一下它的 connect 函数,这个函数的返回值就是一个高阶组件,它接受一个组件并返回一个新组件。

到此,三种主流的创建新组件的方式已经总结完毕,这些内容也是我自己在学习实践过程中的一些总结,写下来分享给大家,希望共同进步。