06-高级模式与实战项目——02. 高阶组件 - HOC模式

📅 2026/7/6 3:35:33
06-高级模式与实战项目——02. 高阶组件 - HOC模式
02. 高阶组件 - HOC模式概述高阶组件Higher-Order ComponentHOC是 React 中用于复用组件逻辑的高级技术。它是一个接收组件并返回新组件的函数常用于横切关注点如认证、日志、数据获取。维度内容What接收组件并返回增强后新组件的函数Why横切关注点日志、认证、数据获取的复用When需要为多个组件添加相同功能时Where组件定义时包裹增强Who需要横切逻辑复用的开发者Howconst EnhancedComponent withAuth(Component)1. 什么是高阶组件1.1 基本概念高阶组件是一个函数接受一个组件作为参数返回一个增强后的新组件。// HOC 的基本形式 function withExtraProps(WrappedComponent) { return function EnhancedComponent(props) { const extraProps { extra: data }; return WrappedComponent {...props} {...extraProps} /; }; } // 使用 const EnhancedComponent withExtraProps(MyComponent);1.2 为什么需要 HOC// ❌ 问题多个组件需要相同的认证逻辑 function Dashboard() { const [user, setUser] useState(null); useEffect(() { const token localStorage.getItem(token); if (token) { fetchUser(token).then(setUser); } }, []); if (!user) return Login /; return div仪表盘/div; } function Profile() { // 相同的认证逻辑重复 const [user, setUser] useState(null); // ... 重复代码 }// ✅ 使用 HOC 复用认证逻辑 function withAuth(WrappedComponent) { return function AuthenticatedComponent(props) { const [user, setUser] useState(null); const [loading, setLoading] useState(true); useEffect(() { const token localStorage.getItem(token); if (token) { fetchUser(token).then(user { setUser(user); setLoading(false); }); } else { setLoading(false); } }, []); if (loading) return div加载中.../div; if (!user) return Login /; return WrappedComponent {...props} user{user} /; }; } // 使用 const DashboardWithAuth withAuth(Dashboard); const ProfileWithAuth withAuth(Profile);2. 常见 HOC 模式2.1 条件渲染 HOC// 认证 HOC function withAuth(WrappedComponent) { return function WithAuthComponent(props) { const isAuthenticated !!localStorage.getItem(token); if (!isAuthenticated) { return Navigate to/login /; } return WrappedComponent {...props} /; }; } // 权限 HOC function withPermission(WrappedComponent, requiredPermission) { return function WithPermissionComponent(props) { const userPermissions getUserPermissions(); if (!userPermissions.includes(requiredPermission)) { return div无权限访问/div; } return WrappedComponent {...props} /; }; }2.2 数据获取 HOCfunction withData(WrappedComponent, fetchData) { return function WithDataComponent(props) { const [data, setData] useState(null); const [loading, setLoading] useState(true); const [error, setError] useState(null); useEffect(() { fetchData(props) .then(setData) .catch(setError) .finally(() setLoading(false)); }, [props]); if (loading) return div加载中.../div; if (error) return div错误: {error.message}/div; return WrappedComponent {...props} data{data} /; }; } // 使用 const UserListWithData withData( UserList, () fetch(/api/users).then(res res.json()) ); const PostDetailWithData withData( PostDetail, (props) fetch(/api/posts/${props.id}).then(res res.json()) );2.3 日志 HOCfunction withLogging(WrappedComponent) { return function WithLoggingComponent(props) { useEffect(() { console.log(${WrappedComponent.name} 已挂载); return () { console.log(${WrappedComponent.name} 已卸载); }; }, []); useEffect(() { console.log(${WrappedComponent.name} 更新, props); }); return WrappedComponent {...props} /; }; } // 使用 const UserProfileWithLogging withLogging(UserProfile);2.4 样式 HOCfunction withStyles(WrappedComponent, styles) { return function WithStylesComponent(props) { return ( div className{styles.container} WrappedComponent {...props} className{styles.inner} / /div ); }; } // 或使用 CSS-in-JS function withTheme(WrappedComponent) { return function WithThemeComponent(props) { const theme useContext(ThemeContext); return WrappedComponent {...props} theme{theme} /; }; }3. 组合多个 HOC3.1 链式调用// 组合多个 HOC const EnhancedComponent withAuth(withData(withLogging(MyComponent))); // 使用 compose 工具函数 import { compose } from redux; const enhance compose( withAuth, withData(fetchUser), withLogging, withStyles(styles) ); const EnhancedComponent enhance(MyComponent);3.2 自定义 composefunction compose(...hocs) { return function(Component) { return hocs.reduceRight((acc, hoc) hoc(acc), Component); }; } // 使用 const enhance compose( withAuth, withData(fetchUser), withLogging ); const EnhancedComponent enhance(MyComponent);4. HOC 注意事项4.1 静态方法丢失// ❌ 问题静态方法会丢失 function withAuth(WrappedComponent) { return class extends React.Component { // WrappedComponent 的静态方法不会传递 }; } // ✅ 解决方案手动复制静态方法 function withAuth(WrappedComponent) { class EnhancedComponent extends React.Component {} // 复制静态方法 Object.assign(EnhancedComponent, WrappedComponent); return EnhancedComponent; } // 或使用 hoist-non-react-statics import hoistNonReactStatics from hoist-non-react-statics; function withAuth(WrappedComponent) { class EnhancedComponent extends React.Component {} hoistNonReactStatics(EnhancedComponent, WrappedComponent); return EnhancedComponent; }4.2 ref 转发// ❌ 问题ref 不会传递 function withAuth(WrappedComponent) { return class extends React.Component { render() { return WrappedComponent {...this.props} /; } }; } // ✅ 使用 forwardRef function withAuth(WrappedComponent) { return React.forwardRef((props, ref) { return WrappedComponent {...props} ref{ref} /; }); }4.3 displayNamefunction withAuth(WrappedComponent) { function EnhancedComponent(props) { return WrappedComponent {...props} /; } // 设置 displayName 方便调试 EnhancedComponent.displayName withAuth(${getDisplayName(WrappedComponent)}); return EnhancedComponent; } function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || Component; }5. 完整示例用户认证系统// HOC: 认证 export function withAuth(WrappedComponent) { function WithAuthComponent(props) { const [user, setUser] useState(null); const [loading, setLoading] useState(true); useEffect(() { const token localStorage.getItem(token); if (token) { fetch(/api/me, { headers: { Authorization: Bearer ${token} }, }) .then(res res.json()) .then(setUser) .finally(() setLoading(false)); } else { setLoading(false); } }, []); if (loading) return div加载中.../div; if (!user) return Navigate to/login /; return WrappedComponent {...props} user{user} /; } WithAuthComponent.displayName withAuth(${getDisplayName(WrappedComponent)}); return WithAuthComponent; } // HOC: 权限 export function withPermission(requiredPermission) { return function(WrappedComponent) { function WithPermissionComponent({ user, ...props }) { if (!user.permissions.includes(requiredPermission)) { return div无权限访问此页面/div; } return WrappedComponent {...props} user{user} /; } WithPermissionComponent.displayName withPermission(${getDisplayName(WrappedComponent)}); return WithPermissionComponent; }; } // HOC: 数据 function withData(fetchData) { return function(WrappedComponent) { function WithDataComponent(props) { const [data, setData] useState(null); const [loading, setLoading] useState(true); useEffect(() { fetchData(props) .then(setData) .finally(() setLoading(false)); }, []); if (loading) return div加载中.../div; return WrappedComponent {...props} data{data} /; } WithDataComponent.displayName withData(${getDisplayName(WrappedComponent)}); return WithDataComponent; }; } // 组合使用 const DashboardPage compose( withAuth, withPermission(admin), withData(() fetch(/api/dashboard).then(res res.json())) )(Dashboard);6. 总结核心要点要点说明定义接收组件返回新组件的函数核心价值横切关注点复用适用场景认证、权限、日志、数据获取注意事项静态方法、ref 转发、displayName记忆口诀HOC 函数包组件横切逻辑都能办认证权限和数据复用起来真方便静态方法要复制ref 转发不能忘7. 相关资源React HOC 文档高阶组件详解