注意事项
使用高阶组件需要注意以下事项。
(1)为了在开发和调试阶段更好地区别包装了不同组件的高阶组件,需要对高阶组件的显示名称做自定义处理。常用的处理方式是,把被包装组件的显示名称也包到高阶组件的显示名称中。以 withPersistentData 为例:
function withPersistentData(WrappedComponent) {
return class extends Component {
// 结合被包装组件的名称,自定义高阶组件的名称
static displayName = `HOC(${getDisplayName(WrappedComponent)})`;
render() {
//...
}
}
}
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
(2)不要在组件的 render 方法中使用高阶组件,尽量也不要在组件的其他生命周期方法中使用高阶组件。因为调用高阶组件,每次都会返回一个新的组件,于是每次 render,前一次高阶组件创建的组件都会被卸载(unmount),然后重新挂载(mount)本次创建的新组件,既影响效率,又丢失了组件及其子组件的状态。例如:
render() {
// 每次 render,enhance 都会创建一个新的组件,尽管被包装的组件没有变
const EnhancedComponent = enhance(MyComponent);
// 因为是新的组件,所以会经历旧组件的卸载和新组件的重新挂载
return <EnhancedComponent />;
}
所以,高阶组件最适合使用的地方是在组件定义的外部,这样就不会受到组件生命周期的影响。
(3)如果需要使用被包装组件的静态方法,那么必须手动复制这些静态方法。因为高阶组件返回的新组件不包含被包装组件的静态方法。例如:
// WrappedComponent 组件定义了一个静态方法 staticMethod
WrappedComponent.staticMethod = function () {
//...
}
function withHOC(WrappedComponent) {
class Enhance extends React.Component {
//...
}
// 手动复制静态方法到 Enhance 上
Enhance.staticMethod = WrappedComponent.staticMethod;
return Enhance;
}
(4)Refs 不会被传递给被包装组件。尽管在定义高阶组件时,我们会把所有的属性都传递给被包装组件,但是 ref 并不会传递给被包装组件。如果在高阶组件的返回组件中定义了 ref,那么它指向的是这个返回的新组件,而不是内部被包装的组件。如果希望获取被包装组件的引用,那么可以自定义一个属性,属性的值是一个函数,传递给被包装组件的 ref。下面的例子就是用 inputRef 这个属性名代替常规的 ref 命名:
function FocusInput({ inputRef, ...rest }) {
// 使用高阶组件传递的 inputRef 作为 ref 的值
return <input ref={inputRef} {...rest} />;
}
// enhance 是一个高阶组件
const EnhanceInput = enhance(FocusInput);
// 在一个组件的 render 方法中,自定义属性 inputRef 代替 ref,
// 保证 inputRef 可以传递给被包装组件
return (<EnhanceInput
inputRef={(input) => {
this.input = input
}}>
)
// 组件内,让 FocusInput 自动获取焦点
this.input.focus();
(5)与父组件的区别。高阶组件在一些方面和父组件很相似。例如,我们完全可以把高阶组件中的逻辑放到一个父组件中去执行,执行完成的结果再传递给子组件,但是高阶组件强调的是逻辑的抽象。高阶组件是一个函数,函数关注的是逻辑;父组件是一个组件,组件主要关注的是 UI/DOM。如果逻辑是与 DOM 直接相关的,那么这部分逻辑适合放到父组件中实现;如果逻辑是与 DOM 不直接相关的,那么这部分逻辑适合使用高阶组件抽象,如数据校验、请求发送等。