跳到主要内容

高阶组件

React 中, 逻辑复用组件组合 是核心设计模式。高阶组件 ( HOC )、 Render Props 和组合模式是三种主流实现方式。

信息

随着 Hooks 的出现,许多 HOC 和 Render Props 的场景已被更简洁的 自定义 Hook 取代。

一、高阶组件(HOC,Higher-Order Component)

高阶组件是一个 函数 ,接收一个组件,返回一个新的增强的组件,用于复用逻辑(如权限校验、日志记录)。它通过 包装原组件 来注入额外逻辑或属性。

1. 携带日志功能的高级组件

携带日志功能的高级组件
function withLogging(WrappedComponent) {
return class extends React.Component {
componentDidMount() {
console.log(`组件 ${WrappedComponent.name} 已挂载`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
}

2. withAuth 实现(权限控制)

  • 使用 React Context 管理全局认证状态(如用户信息)
  • HOC 内部消费 AuthContext ,获取用户认证状态
  • 根据认证状态决定渲染原组件或重定向
// 定义 Auth Context (用于全局状态管理)
const AuthContext = React.createContext<{
isAuthenticated: boolean;
loading: boolean;
}>({
isAuthenticated: false,
loading: true,
});

// 实现 withAuth HOC
function withAuth<P extends object>(WrappedComponent: React.ComponentType<P>) {
return function (props: P) {
// 从 AuthContext 获取认证状态
const { isAuthenticated, loading } = useContext(AuthContext);

// 加载后显式占位符(可选)
if (loading) {
return <div>loading</div>;
}

// 未认证时重定向至登录页
if (!isAuthenticated) {
// 假设使用 react-router-dom 的 Navigate
return <Navigate to="/login" replace />;
}
// 已认证的渲染原组件,并传递所有的 props
return <WrappedComponent {...props} />;
};
}

// 使用示例
const ProtectedPage = () => <div>这是受保护的内页</div>;

// 用 withAuth 包装组件,获得权限控制能力
export default withAuth(ProtectedPage);

二、渲染属性(Render Props)

Render Props 是一种 通过函数类型的 Props 控制渲染内容 的模式,实现逻辑复用。组件通过一个名为 render (或 children )的函数 Props ,将内部数据传递给外部,由外部决定如何渲染。

  • 组件内部管理状态(如数据、加载状态)
  • 通过 render 函数(或 children 作为函数)暴露状态给外部
  • 外部组件完全控制渲染逻辑

1. 用户示例

用户信息示例
interface User {
id: number;
name: string;
}

interface UserDataProps {
render: (user: User | null, loading: boolean) => React.ReactNode;
}

const UserData: Rect.FC<UserDataProps> = ({ render }) => {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState<true>;

// 模拟获取用户数据
useEffect(() => {
fetchUser().then(data => {
setUser(data);
setLoading(false);
});
}, []);

// 通过 render 函数将数据传递给外部
return <>render(user, loading)</>;
};

// 使用示例
const UserProfile = () => (
<UserData
render={(user, loading) => {
if (loading) return <div>加载用户信息中</div>;
return user ? <div>你好, {user.name}</div> : <div>当前用户不存在</div>;
}}
/>
);

2. 请求示例

简单的请求示例
class DataFetcher extends React.Component {
state = { data: null };

componentDidMount() {
fetch('http://api.lmssee.com/data')
.then(res => res.json())
.then(data => this.setState({ data }));
}

render() {
return this.props.render(this.state.data); // 通过 render 函数传递数据
}
}

// 使用

<DataFetcher
render={data => (data ? <div>{data.name}</div> : <p>加载中</p>)}
/>;

三、自定义 Hooks (Function Component 专属)

提取可复用的状态逻辑,避免重复代码(代替 HOC 和 Render Props)。它强调“组合优于继承”。

function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
fetch(url)
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, [url]);

return { data, loading };
}

// 使用自定义 Hook
function MyComponent() {
const { data, loading } = useFetch('https://lmssee.com/data');
return loading ? <p>加载中。。。</p> : <div>{data.name}</div>;
}

四、 HOC 与 Render Props 得优缺点对比

维度高阶组件 ( HOC )Render Props
逻辑复用✅ 独立于组件树,逻辑集中管理(如 withAuth 可复用于多个组件)✅ 逻辑与渲染解耦,通过函数 prop 灵活传递
组件树结构❌ 可能导致嵌套地狱(多层 HOC 包装)❌ 可能因 render 函数嵌套问题导致 JSX 层级过深(但比 HOC 浅)
Props 冲突❌ 可能覆盖原组件的 Props (需谨慎合并 props )✅ 无 props 冲突(外部通过函数参数获取数据,不影响原组件 Props)
调试难度❌ 调试时组件层级复杂 ( React DevTools 显示多层包转组件)✅ 组件树更扁平( Render Props 通常在同一层级渲染)
类型支持⚠️ 需要泛型定义,增加配置复杂度✅ 类型更佳直观(函数参数明确输入输出)
学习成本⚠️ 需理解 Context、 组件包装机制✅ 符合 React 基础(函数 Prop ),学习成本低
使用场景跨组件无渲染逻辑(如权限、路由、日志)需要细颗粒度控制渲染逻辑(如数据获取后自定义 UI)