# 高阶组件 HOC

  • 高阶组件是函数,参数是组件,返回值是新组件

  • 组件是将 props 转为 UI,高阶组件是将组件转换为另一个组件

  • HOC 是纯函数,无副作用

# 解决横切面关注点问题

  • 不会修改传入的组件,不会使用继承来复制其行为

  • HOC 不需要关心数据的使用方式和原因,被包装组件也不需要关心数据来源

const CommentListWithSubscription = withSubscription(
  CommentList,
  (DataSource) => DataSource.getComments()
);

const BlogPostWithSubscription = withSubscription(
  BlogPost,
  (DataSource, props) => DataSource.getBlogPost(props.id)
);
1
2
3
4
5
6
7
8
9

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

function logProps(InputComponent) {
  InputComponent.prototype.componentDidUpdate = function(prevProps) {
    console.log('Current props: ', this.props);
    console.log('Previous props: ', prevProps);
  };
  // 返回原始的 input 组件,暗示它已经被修改。
  return InputComponent;
}

// 每次调用 logProps 时,增强组件都会有 log 输出。
const EnhancedComponent = logProps(InputComponent);
1
2
3
4
5
6
7
8
9
10
11
function logProps(WrappedComponent) {
  return class extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('Current props: ', this.props);
      console.log('Previous props: ', prevProps);
    }
    render() {
      // 将 input 组件包装在容器中,而不对其进行修改。Good!
      return <WrappedComponent {...this.props} />;
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 注意事项

# 不要在 render 方法中使用 HOC

React 的 diff 算法(称为协调)使用组件标识来确定它是应该更新现有子树还是将其丢弃并挂载新子树。 如果从 render 返回的组件与前一个渲染中的组件相同(===),则 React 通过将子树与新子树进行区分来递归更新子树。 如果它们不相等,则完全卸载前一个子树

render() {
  // 每次调用 render 函数都会创建一个新的 EnhancedComponent
  // EnhancedComponent1 !== EnhancedComponent2
  const EnhancedComponent = enhance(MyComponent);
  // 这将导致子树每次渲染都会进行卸载,和重新挂载的操作!
  return <EnhancedComponent />;
}
1
2
3
4
5
6
7

# 务必复制静态方法

  • 使用 HOC 返回的新组件没有原始组件的任何静态方法
// 定义静态函数
WrappedComponent.staticMethod = function() {/*...*/}
// 现在使用 HOC
const EnhancedComponent = enhance(WrappedComponent);

// 增强组件没有 staticMethod
typeof EnhancedComponent.staticMethod === 'undefined' // true
1
2
3
4
5
6
7
  • 解决方法是复制
function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  // 必须准确知道应该拷贝哪些方法 :(
  Enhance.staticMethod = WrappedComponent.staticMethod;
  return Enhance;
}
1
2
3
4
5
6

# Refs 不会被传递

  • ref 并不是 prop ,跟 key 一样