是什么
高阶组件并不是 React API,只是一种编程技巧而已。
从形式上看,高阶组件是参数和返回值都是组件的纯函数。
从功能上看,高阶组件就是给原组件添加功能,将其转变成新的组件。
解决什么问题
HOC 可以用来解决“横切关注点问题”,即不同组件间逻辑复用的问题。
可以将组件间可复用的逻辑使用 HOC 来封装起来,各个组件将自己传递给 HOC 后,HOC 将返回新的添加了特定功能的组件。
约定
props 传递
高阶组件需要将与自己无关的 props 传递给被包装的组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| render() { // 过滤掉 HOC 额外的 props const { extraProp, ...passThroughProps } = this.props;
// 将 props 注入到被包装的组件中。 // 通常为 state 的值或者实例方法。 const injectedProp = someStateOrInstanceMethod;
// 将 props 传递给被包装组件 return ( <WrappedComponent injectedProp={injectedProp} {...passThroughProps} /> ); }
|
调试
需要设置新组件的显示名称,以方便调试
1 2 3 4 5 6 7 8 9
| function withSubscription(WrappedComponent) { class WithSubscription extends React.Component {/* ... */} WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`; return WithSubscription; }
function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; }
|
注意事项
不要在 render 中调用 HOC
可以在组件的生命周期方法中调用HOC,但不要在 render 中调用 HOC,否则将导致子树每次渲染都会进行卸载和重新挂载的操作!
1 2 3 4 5 6 7 8 9 10 11
| /* 不要在 render 中调用 HOC */ render() { // 下面语句会使得每次执行 render 函数都会创建一个新的 EnhancedComponent // 导致 EnhancedComponentOld !== EnhancedComponentNew // 进而导致子树每次渲染都会进行卸载和重新挂载的操作! const EnhancedComponent = enhance(MyComponent); return <EnhancedComponent />; }
|
组件静态方法
需要对组件静态方法进行额外复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| /* 被包装后的组件默认没有原始组件的静态方法 可以用下面两种方式进行处理 */
//方式一:返回前将静态方法拷贝给包裹后的组件 //可以使用 hoist-non-react-statics 自动拷贝所有非 React 静态方法: import hoistNonReactStatic from 'hoist-non-react-statics'; function enhance(WrappedComponent) { class Enhance extends React.Component {/*...*/} hoistNonReactStatic(Enhance, WrappedComponent); return Enhance; }
//方式二:将原始组件的静态方法额外导出,使用的时候再导入 MyComponent.someFunction = someFunction; export default MyComponent;
// ...单独导出该方法... export { someFunction };
// ...并在要使用的组件中,import 它们 import MyComponent, { someFunction } from './MyComponent.js';
|
Refs 不会被传递
props 中没有包含 ref 属性,故不会被传递,可以通过使用 React.forwardRef
API 解决此问题。