高阶组件是React中一种基于组件组合的设计模式,本质上是一个函数,它接收一个组件作为参数,并返回一个新的组件。
原始React组件
HOC添加额外功能
增强的React组件
import React from 'react';
// 高阶组件函数
const withEnhancement = (WrappedComponent) => {
// 返回一个新的组件类
class EnhancedComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
// 添加额外的状态
isEnhanced: true,
data: null
};
}
componentDidMount() {
// 添加生命周期方法
console.log('组件已被增强');
}
// 添加额外的方法
fetchData = () => {
// 数据获取逻辑
};
render() {
// 将额外的props传递给被包装的组件
const enhancedProps = {
...this.props,
isEnhanced: this.state.isEnhanced,
fetchData: this.fetchData,
hocData: '来自HOC的数据'
};
return <WrappedComponent {...enhancedProps} />;
}
}
// 设置displayName以便调试
EnhancedComponent.displayName = `WithEnhancement(${getDisplayName(WrappedComponent)})`;
return EnhancedComponent;
};
// 辅助函数:获取组件显示名称
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
// 使用HOC增强组件
const OriginalComponent = (props) => {
return (
<div>
<h3>原始组件</h3>
<p>HOC传递的数据: {props.hocData}</p>
{props.isEnhanced && <p>✅ 此组件已被增强</p>}
</div>
);
};
const EnhancedComponent = withEnhancement(OriginalComponent);
// 使用增强后的组件
function App() {
return <EnhancedComponent someProp="来自App的数据" />;
}
保护路由,验证用户权限
const withAuth = (WrappedComponent) => {
return class extends React.Component {
componentDidMount() {
if (!isAuthenticated()) {
redirectToLogin();
}
}
render() {
return isAuthenticated() ?
<WrappedComponent {...this.props} /> :
<LoginPrompt />;
}
};
};
统一处理数据加载逻辑
const withData = (url) => (WrappedComponent) => {
return class extends React.Component {
state = { data: null, loading: true };
async componentDidMount() {
const data = await fetch(url);
this.setState({ data, loading: false });
}
render() {
return <WrappedComponent
data={this.state.data}
loading={this.state.loading}
{...this.props}
/>;
}
};
};
跟踪组件渲染性能
const withPerformance = (WrappedComponent) => {
return class extends React.Component {
componentDidMount() {
this.startTime = performance.now();
}
componentDidUpdate() {
const renderTime = performance.now() - this.startTime;
console.log(`${WrappedComponent.name} 渲染耗时: ${renderTime}ms`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
};
提供多语言支持
const withI18n = (WrappedComponent) => {
return class extends React.Component {
t = (key) => {
const translations = {
'welcome': '欢迎',
'submit': '提交'
};
return translations[key] || key;
};
render() {
return <WrappedComponent
t={this.t}
{...this.props}
/>;
}
};
};
import React from 'react';
const withErrorBoundary = (WrappedComponent) => {
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// 可以在这里记录错误到日志服务
console.error('组件错误:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div style={{
padding: '20px',
border: '2px solid #dc3545',
borderRadius: '8px',
background: '#f8d7da',
color: '#721c24'
}}>
<h3>组件发生错误</h3>
<p>错误信息: {this.state.error.toString()}</p>
<button
onClick={() => this.setState({ hasError: false })}
style={{
padding: '8px 16px',
background: '#dc3545',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
重试
</button>
</div>
);
}
return <WrappedComponent {...this.props} />;
}
}
ErrorBoundary.displayName = `WithErrorBoundary(${WrappedComponent.name})`;
return ErrorBoundary;
};
// 使用示例
const BuggyComponent = () => {
// 故意抛出错误
throw new Error('测试错误');
return <div>正常显示</div>;
};
const SafeComponent = withErrorBoundary(BuggyComponent);
const withMousePosition = (WrappedComponent) => {
return class extends React.Component {
state = {
x: 0,
y: 0
};
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
componentDidMount() {
window.addEventListener('mousemove', this.handleMouseMove);
}
componentWillUnmount() {
window.removeEventListener('mousemove', this.handleMouseMove);
}
render() {
return (
<WrappedComponent
mouseX={this.state.x}
mouseY={this.state.y}
{...this.props}
/>
);
}
};
};
// 使用示例
const DisplayMousePosition = (props) => {
return (
<div style={{ padding: '20px', border: '1px solid #ccc' }}>
<h3>鼠标位置追踪</h3>
<p>X坐标: {props.mouseX}</p>
<p>Y坐标: {props.mouseY}</p>
</div>
);
};
const MouseTrackingComponent = withMousePosition(DisplayMousePosition);
const withLoading = (WrappedComponent) => {
return class extends React.Component {
state = {
loading: true,
data: null
};
async componentDidMount() {
try {
// 模拟API调用
const response = await fetch(this.props.url);
const data = await response.json();
this.setState({ data, loading: false });
} catch (error) {
this.setState({ loading: false });
console.error('数据加载失败:', error);
}
}
render() {
const { loading, data } = this.state;
if (loading) {
return (
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '200px'
}}>
<div className="spinner-border" role="status">
<span className="visually-hidden">加载中...</span>
</div>
</div>
);
}
return <WrappedComponent data={data} {...this.props} />;
}
};
};
// 使用示例
const UserList = ({ data }) => {
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
const UserListWithLoading = withLoading(UserList);
import React from 'react';
// 多个HOC函数
const withLogger = (WrappedComponent) => {
return class extends React.Component {
componentDidMount() {
console.log(`组件 ${WrappedComponent.name} 已挂载`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
};
const withTimer = (WrappedComponent) => {
return class extends React.Component {
state = { time: new Date() };
componentDidMount() {
this.timerId = setInterval(() => {
this.setState({ time: new Date() });
}, 1000);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
render() {
return <WrappedComponent
currentTime={this.state.time}
{...this.props}
/>;
}
};
};
const withStyle = (styles) => (WrappedComponent) => {
return class extends React.Component {
render() {
return (
<div style={styles}>
<WrappedComponent {...this.props} />
</div>
);
}
};
};
// 基础组件
const BasicComponent = (props) => {
return (
<div>
<h3>基础组件</h3>
<p>当前时间: {props.currentTime.toLocaleTimeString()}</p>
<p>其他属性: {props.message}</p>
</div>
);
};
// 组合多个HOC的方式1:嵌套调用
const EnhancedComponent1 = withLogger(withTimer(withStyle({
padding: '20px',
border: '2px solid #007bff',
borderRadius: '8px'
})(BasicComponent)));
// 组合多个HOC的方式2:使用compose函数(类似redux)
const compose = (...funcs) => {
return funcs.reduce((a, b) => (...args) => a(b(...args)));
};
const enhance = compose(
withLogger,
withTimer,
withStyle({
padding: '20px',
border: '2px solid #28a745',
borderRadius: '8px'
})
);
const EnhancedComponent2 = enhance(BasicComponent);
// 使用
function App() {
return (
<div>
<EnhancedComponent1 message="嵌套调用方式" />
<EnhancedComponent2 message="compose函数方式" />
</div>
);
}
确保所有原始props都传递给被包装组件
render() {
// 正确:透传所有props
return <WrappedComponent {...this.props} />;
// 错误:可能会丢失props
// return <WrappedComponent />;
}
EnhancedComponent.displayName =
`WithEnhancement(${getDisplayName(WrappedComponent)})`;
使用组合而非继承,避免直接修改组件原型
function copyStaticMethods(source, target) {
Object.keys(source).forEach(key => {
if (typeof source[key] === 'function') {
target[key] = source[key];
}
});
}
const withRefForwarding = (WrappedComponent) => {
class EnhancedComponent extends React.Component {
render() {
const { forwardedRef, ...rest } = this.props;
return <WrappedComponent ref={forwardedRef} {...rest} />;
}
}
return React.forwardRef((props, ref) => {
return <EnhancedComponent {...props} forwardedRef={ref} />;
});
};
// HOC实现方式
const withDataHOC = (url) => (WrappedComponent) => {
return class extends React.Component {
state = { data: null };
// ... 数据获取逻辑
render() {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
};
// Render Props实现方式
class DataProvider extends React.Component {
state = { data: null };
// ... 相同的数据获取逻辑
render() {
return this.props.children(this.state.data);
}
}
// 使用Render Props
<DataProvider url="/api/data">
{(data) => data ? <Component data={data} /> : <Loading />}
</DataProvider>
| 库名 | 用途 | 特点 |
|---|---|---|
| recompose | React工具库 | 提供大量实用的HOC,已被Hooks替代 |
| react-redux | 状态管理 | connect()函数是最著名的HOC |
| react-router | 路由管理 | withRouter()提供路由相关props |
| react-i18next | 国际化 | withTranslation()提供翻译功能 |
| material-ui | UI组件库 | 使用HOC增强样式和功能 |