高阶组件存在的问题

  • 静态方法丢失
  • refs 属性不能透传
  • 反向继承不能保证完整的子组件树被解析
静态方法丢失

因为原始组件被包裹于一个容器组件内,也就意味着新组件会没有原始组件的任何静态方法:

1
2
3
4
5
6
// 定义静态方法
WrappedComponent.staticMethod = function() {}
// 使用高阶组件
const EnhancedComponent = HigherOrderComponent(WrappedComponent);
// 增强型组件没有静态方法
typeof EnhancedComponent.staticMethod === 'undefined' // true

所以必须将静态方法做拷贝:

1
2
3
4
5
6
function HigherOrderComponent(WrappedComponent) {
class Enhance extends React.Component {}
// 必须得知道要拷贝的方法
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}

但是这么做的一个缺点就是必须知道要拷贝的方法是什么,不过 React 社区实现了一个库 hoist-non-react-statics 来自动处理,它会 自动拷贝所有非 React 的静态方法

1
2
3
4
5
6
7
import hoistNonReactStatic from 'hoist-non-react-statics';

function HigherOrderComponent(WrappedComponent) {
class Enhance extends React.Component {}
hoistNonReactStatic(Enhance, WrappedComponent);
return Enhance;
}
refs 属性不能透传

一般来说高阶组件可以传递所有的 props 给包裹的组件 WrappedComponent,但是有一种属性不能传递,它就是 ref。与其他属性不同的地方在于 React 对其进行了特殊的处理。

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

那如果有一定要传递 ref 的需求呢,别急,React 为我们提供了一个名为 React.forwardRef 的 API 来解决这一问题(在 React 16.3 版本中被添加):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function withLogging(WrappedComponent) {
class Enhance extends WrappedComponent {
componentWillReceiveProps() {
console.log('Current props', this.props);
console.log('Next props', nextProps);
}
render() {
const {forwardedRef, ...rest} = this.props;
// 把 forwardedRef 赋值给 ref
return <WrappedComponent {...rest} ref={forwardedRef} />;
}
};

// React.forwardRef 方法会传入 props 和 ref 两个参数给其回调函数
// 所以这边的 ref 是由 React.forwardRef 提供的
function forwardRef(props, ref) {
return <Enhance {...props} forwardRef={ref} />
}

return React.forwardRef(forwardRef);
}
const EnhancedComponent = withLogging(SomeComponent);
反向继承不能保证完整的子组件树被解析

React 组件有两种形式,分别是 class 类型和 function 类型(无状态组件)。

我们知道反向继承的渲染劫持可以控制 WrappedComponent 的渲染过程,也就是说这个过程中我们可以对 elements tree、state、props 或 render() 的结果做各种操作。

但是如果渲染 elements tree 中包含了 function 类型的组件的话,这时候就不能操作组件的子组件了。