将 HOC 重构为自定义钩子
逐步将 withAuth HOC 转换为 useAuth 钩子。
将 HOC 重构为自定义钩子 是 CoddyKit 上的免费 React Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Academy 课程共包含 4 节课。
欢迎
在本课中,您将逐步把 withAuth HOC 重构为 useAuth 自定义钩子,并准确了解为什么钩子版本更简洁、更易于测试。
withAuth HOC(起点)
该 HOC 接收一个组件,检查用户是否通过身份验证;如果没有,则将用户重定向到 /login。它会将用户注入为一个属性。
function withAuth(Component) {
return function AuthGuard(props) {
const { user, loading } = useAuthState();
if (loading) return <Spinner />;
if (!user) return <Navigate to="/login" />;
return <Component {...props} user={user} />;
};
}
const ProtectedPage = withAuth(MyPage);HOC 存在的问题
HOC 存在以下问题:(1) 会在 DevTools 中添加一个包装器;(2) 会隐式注入 `user` 属性,组件必须知道要接收它;(3) 如果不渲染完整的 HOC 包装器,就很难测试;(4) 很难与其他钩子轻松组合。
第 1 步:将逻辑提取到钩子中
将 HOC 中的所有逻辑移动到自定义钩子中。该钩子会直接返回身份验证状态,不进行包装,也不注入属性。
function useAuth() {
const { user, loading } = useAuthState();
return { user, loading, isAuthenticated: !!user };
}第 2 步:在钩子中处理重定向
将重定向逻辑移动到单独的钩子(例如 useRequireAuth)中,由它在内部调用 navigate 并返回用户。
function useRequireAuth(redirectTo = '/login') {
const { user, loading } = useAuth();
const navigate = useNavigate();
useEffect(() => {
if (!loading && !user) navigate(redirectTo);
}, [user, loading, navigate, redirectTo]);
return { user, loading };
}第 3 步:更新组件
在组件内部直接调用钩子,以取代 HOC 包装器。组件会显式调用 useRequireAuth 并处理加载状态。现在,它的依赖关系清晰可见。
function MyPage() {
const { user, loading } = useRequireAuth();
if (loading) return <Spinner />;
return <p>Hello, {user.name}</p>;
}对比优势
钩子版本具有以下优势:(1) DevTools 中没有额外的包装器;(2) 依赖关系明确——您可以在组件中看到 `useRequireAuth`;(3) 可以通过模拟钩子进行测试;(4) 可以与同一组件中的其他钩子组合。
测试钩子版本
在测试中替换 useRequireAuth 的模块导出,以模拟该钩子。组件测试只需渲染组件并断言输出,无需设置 HOC。
vi.mock('./useRequireAuth', () => ({
useRequireAuth: () => ({ user: mockUser, loading: false })
}));
render(<MyPage />);
expect(screen.getByText('Hello, Alice')).toBeInTheDocument();保留 HOC 的场景
如果您需要以声明式方式保护许多组件(例如在路由定义中),并且不想在每个组件中添加钩子调用,则可以保留 HOC。您甚至可以在钩子之上构建 HOC。
function withAuth(Component) {
return function (props) {
const { user, loading } = useRequireAuth();
if (loading) return <Spinner />;
return <Component {...props} user={user} />;
};
}两全其美
最终架构是:提供一个可在组件中直接使用的 useRequireAuth 钩子,并在其之上构建一个轻量的 withAuth HOC,用于以声明式方式保护路由。两者共享同一套钩子逻辑。
快速检查
与使用 withAuth HOC 包装组件相比,在组件内部调用 useRequireAuth 的主要优势是什么?
回顾
将 HOC 重构为钩子的方法是:将逻辑提取到自定义钩子中,将副作用(例如重定向)移动到钩子中,并在组件内直接调用钩子。当需要声明式包装时,再在钩子之上构建一个轻量 HOC。
课程完成
恭喜您!您完成了**React 模式:渲染属性与 HOC**。现在,您已经理解这两种经典模式及其权衡,也知道如何将它们重构为现代自定义钩子。
常见问题解答
「将 HOC 重构为自定义钩子」课时是免费的吗?
是的 — 「将 HOC 重构为自定义钩子」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Academy 课程共包含 4 节课。
「将 HOC 重构为自定义钩子」这节课中我会学到什么?
逐步将 withAuth HOC 转换为 useAuth 钩子。 你通过在浏览器中直接运行的动手代码来练习 React Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「将 HOC 重构为自定义钩子」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Academy 课中编写并运行代码吗?
能。每节 React Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- Render Props 模式
- 高阶组件(HOC)详解
- HOC、Render Props 与自定义钩子
- 将 HOC 重构为自定义钩子