高阶组件

 高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。

   高阶组件就是一个没有副作用的纯函数。

  不要改变原始组件,使用组合

   例如:Redux的connect方法

// This function takes a component...
function withSubscription(WrappedComponent, selectData) {
  // ...and returns another component...
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.handleChange = this.handleChange.bind(this);
      this.state = {
        data: selectData(DataSource, props)
      };
    }

    componentDidMount() {
      // ... that takes care of the subscription...
      DataSource.addChangeListener(this.handleChange);
    }

    componentWillUnmount() {
      DataSource.removeChangeListener(this.handleChange);
    }

    handleChange() {
      this.setState({
        data: selectData(DataSource, this.props)
      });
    }

    render() {
      // ... and renders the wrapped component with the fresh data!
      // Notice that we pass through any additional props
      return <WrappedComponent data={this.state.data} {...this.props} />;
    }
  };
}

约定:贯穿传递不相关props属性给被包裹的组件

render() {
  // 过滤掉专用于这个阶组件的props属性,
  // 不应该被贯穿传递
  const { extraProp, ...passThroughProps } = this.props;

  // 向被包裹的组件注入props属性,这些一般都是状态值或
  // 实例方法
  const injectedProp = someStateOrInstanceMethod;

  // 向被包裹的组件传递props属性
  return (
    <WrappedComponent
      injectedProp={injectedProp}
      {...passThroughProps}
    />
  );
}

 

约定:最大化的组合性

// 不要这样做……
const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent))

// ……你可以使用一个函数组合工具
// compose(f, g, h) 和 (...args) => f(g(h(...args)))是一样的
const enhance = compose(
  // 这些都是单独一个参数的高阶组件
  withRouter,
  connect(commentSelector)
)
const EnhancedComponent = enhance(WrappedComponent)

单独一个参数的高阶组件,类似 connect 函数返回的,签名是Component => Component。输入和输出类型相同的函数确实是很容易组合在一起。

约定:包装显示名字以便于调试

function withSubscription(WrappedComponent) {
  class WithSubscription extends React.Component {/* ... */}
  WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
  return WithSubscription;
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}

React Developer Tools便于调试,

 

注意:

不要在render方法内使用高阶组件

原因: (差分算法,渲染性能问题)

render() {
  // 每一次渲染,都会创建一个新的EnhancedComponent版本
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // 那引起每一次都会使子对象树完全被卸载/重新加载
  return <EnhancedComponent />;
}

必须将静态方法做拷贝

 1)可借助hoist-non-react-statics

function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  // 必须得知道要拷贝的方法 :(
  Enhance.staticMethod = WrappedComponent.staticMethod;
  return Enhance;
}

2)分别导出组件自身的静态方法。

// Instead of...
MyComponent.someFunction = someFunction;
export default MyComponent;

// ...export the method separately...
export { someFunction };

// ...and in the consuming module, import both
import MyComponent, { someFunction } from './MyComponent.js';

Refs属性不能贯穿传递

如果你向一个由高阶组件创建的组件的元素添加ref应用,那么ref指向的是最外层容器组件实例的,而不是被包裹的组件。

现在我们提供一个名为 React.forwardRef 的 API 来解决这一问题(在 React 16.3 版本中)

posted @ 2019-03-11 15:24  Lyra李  阅读(593)  评论(0编辑  收藏  举报