高阶组件:是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API的一部分,它是一种基于 React 的组合特性而形成的一种设计模式
HOC 的作用之一就是解决大量的代码复用、逻辑复用的问题,还有一个重要的作用就是让 props 中放入一些开发者需要的东西
HOC 是以组件为参数,返回组件的函数。返回的组件把传进去的组件进行功能强化
常用的 HOC 有:属性代理和反向继承两种
高阶组件之属性代理
属性代理就是用代理组件包裹原始组件,在代理组件上,可以做一些对原始组件的强化操作,这里要注意代理返回的是一个新组件,被包裹包裹的原始组件将在新的组件内被挂在
javascript
import React from 'react';
function HOC(WrapComponent) {
return class advance extends React.Component {
state = {
name: 'my name is Vera',
};
render() {
return <WrapComponent {...this.props} name={this.state.name} />;
}
};
}
function Test(props) {
console.log(props);
return (
<div {...props}>
<h1>{props.name}</h1>
</div>
);
}
export default HOC(Test);

高阶组件之反向继承
反向继承和属性代理有一定的区别,在于包装后的组件继承了原始组件,所以此时无需再去挂载业务组件
scala
class Index extends React.Component {
render() {
return <div>Hello, Vera</div>;
}
}
function HOC2(Component) {
// 直接继承需要包装的组件
return class wrapComponent extends Component {
// ...
};
}
export default HOC2(Index);
高阶组件的功能:
功能之强化 props,
就是在原始组件的 props 上,加入一些其他的 props,强化原始组件功能。举个例子,为了让组件也可以获取路由对象,进行路由跳转等操作,React-Router 提供了像 WithRouter 这样的 HOC
功能之控制渲染
①渲染错误边界
比如我们使用变量 list 存储一个数组,使用数组的 map 方法来渲染列表,但假如这个list 被置为 null(比如可能从接口中拿到一个 null 并且没有处理这个数据),那么 map 方法就会报错,我们可以用一个 HOC 来处理渲染错误,如果出现错误,就展示 Error
当我们人为点击测试按钮时,就会触发 componentDidCatch 生命周期,来展示托底 UI
请注意:属性代理的 HOC,在挂载原始组件的时候,需要把 props 进行转发,当 Component 父组件绑定给它的 props 时,会被绑定到地里组件 WrapComponent 上,所以在挂载 Component 的时候,需要将 this.props 上的所有属性传递下去
②修改渲染树
javascript
class Index6 extends React.Component {
render() {
return (
<div>
<ul>
<li>React</li>
<li>Vue</li>
<li>Angular</li>
</ul>
</div>
);
}
}
function HOC6(Component) {
return class Advance extends Component {
render() {
const element = super.render();
debugger;
const otherProps = {
name: 'HOC',
};
const appendElement = React.createElement('li', {}, `hello, i am ${otherProps.name}`);
const newChild = React.Children.map(element.props.children.props.children, (child, index) => {
console.log(child, index);
if (index === 2) {
return appendElement;
}
return child;
});
console.log(newChild);
return React.cloneElement(element, element.props, newChild);
}
};
}
export default HOC6(Index6);

通过 jsx 方式修改渲染树,改变渲染的结构
功能之组件赋能
给组件增加一些额外功能,比如组件状态监控,埋点监控等
javascript
/ 对组件内的点击事件进行监听
function ClickHoc(Component) {
return function Wrap(props) {
const dom = useRef(null);
useEffect(() => {
const handleClick = () => console.log('点击了');
dom.current.addEventListener('click', handleClick);
return () => {
dom.current.removeEventListener('click', handleClick);
};
}, []);
return (
<div ref={dom}>
<Component {...props} />
</div>
);
};
}
@ClickHoc
class Index7 extends React.Component {
render() {
return (
<div className="index">
<p>Hello, world</p>
<button>组件内部点击</button>
</div>
);
}
}
export default () => {
return (
<div className="box">
<Index7 />
<button>组件外部点击</button>
</div>
);
};
高阶组件实践:渲染分片
javascript
/**
* 高阶组件事件:渲染分片
*/
function createHoc() {
const renderQueue = [];
return function Hoc(Component) {
// RenderController 用于真正挂在原始组件
function RenderController(props) {
const { renderNextComponent, ...otherProps } = props;
useEffect(() => {
renderNextComponent();
}, []);
return <Component {...otherProps} />;
}
return class Wrap extends React.Component {
constructor() {
super();
this.state = {
isRender: false,
};
const tryRender = () => {
this.setState({
isRender: true,
});
};
if (renderQueue.length === 0) this.isFirstRender = true;
renderQueue.push(tryRender);
}
isFirstRender = false;
renderNextComponent = () => {
if (renderQueue.length > 0) {
console.log('挂载下一个组件');
const nextRender = renderQueue.shift();
setTimeout(() => {
nextRender();
}, 1000);
}
};
componentDidMount() {
this.isFirstRender && this.renderNextComponent();
}
render() {
const { isRender } = this.state;
return isRender ? (
<RenderController {...this.props} renderNextComponent={this.renderNextComponent} />
) : (
<div>...loading</div>
);
}
};
};
}
const loadingHoc = createHoc();
function CompA() {
useEffect(() => {
console.log('组件 A 挂载完成');
});
return <div>模块 A</div>;
}
function CompB() {
useEffect(() => {
console.log('组件 B 挂载完成');
});
return <div>模块 B</div>;
}
function CompC() {
useEffect(() => {
console.log('组件 C 挂载完成');
});
return <div>模块 C</div>;
}
const ComponentA = loadingHoc(CompA);
const ComponentB = loadingHoc(CompB);
const ComponentC = loadingHoc(CompC);
export default function Index8() {
return (
<div>
<ComponentA />
<ComponentB />
<ComponentC />
</div>
);
}
解析:
这段代码实现了一个高阶组件(Higher-Order Component, HOC)工厂函数 createHoc
,用于控制多个组件的顺序渲染。下面是对代码的详细解释:
1. 高阶组件工厂函数 ****createHoc
createHoc
是一个工厂函数,返回一个高阶组件Hoc
- 内部维护一个
renderQueue
数组,用于存储待渲染组件的渲染函数
2. 高阶组件 ****Hoc
- 接收一个
Component
作为参数,返回一个新的包装组件Wrap
- 主要功能是控制组件的顺序渲染
3. ****RenderController ****组件
- 这是实际渲染原始组件
Component
的控制器 - 接收
renderNextComponent
方法作为 props,并在useEffect
中调用它 - 渲染原始组件
Component
并传递所有其他 props
4. ****Wrap ****类组件
这是高阶组件返回的包装组件,主要逻辑包括:
状态管理
isRender
状态控制是否渲染实际内容
渲染队列管理
- 在构造函数中,将
tryRender
方法(用于设置渲染状态)推入renderQueue
- 如果是队列中的第一个组件(
renderQueue.length === 0
),标记为isFirstRender
核心方法
renderNextComponent
:从队列中取出下一个渲染函数并执行,触发下一个组件的渲染componentDidMount
:如果是第一个组件,立即触发渲染
渲染逻辑
- 如果
isRender
为 true,渲染RenderController
(即实际组件) - 否则显示加载状态("...loading")
工作原理
-
当多个组件被这个 HOC 包装时,它们会被依次加入渲染队列
-
只有当前组件渲染完成后,才会触发下一个组件的渲染
-
每个组件会先显示加载状态,直到轮到它渲染时才显示实际内容
使用场景
这种模式适用于需要控制多个组件按顺序加载的场景,比如:
- 资源密集型组件需要分批加载
- 需要确保组件按特定顺序初始化
- 避免同时渲染多个复杂组件导致的性能问题
注意事项
- 这是一个类组件实现,在现代 React 中可以考虑用 hooks 重构
- 需要确保组件卸载时正确处理队列,避免内存泄漏
- 这种顺序渲染可能会影响用户体验,需要权衡利弊使用
这个高阶组件提供了一种优雅的方式来控制多个组件的渲染顺序,是一种高级的 React 模式。