如何遵守Hook规则封装适配useEffect的登录校验自定义Hook?
核心实现思路
自定义Hook仅在组件顶层调用,内部合法调用useHistory等内置Hook,将包含路由跳转、消息提示的副作用逻辑封装为普通函数返回,或者放到Hook内部的useEffect中执行,完全符合Hook调用规则。
方案1:返回校验函数(适配现有调用逻辑,改动最小)
自定义Hook代码
import { useHistory } from "react-router-dom"; // 若使用react-router v6,替换为useNavigate即可 function useLogin() { const history = useHistory(); // 封装后的校验方法为普通函数,可在任意回调/useEffect中调用 const checkLogin = (callback, redirectUrl = "/accounts/login", loginMessage) => { if (!state.user.authenticated) { history.push(redirectUrl); // 未传入自定义提示时调用默认提示 loginMessage ? loginMessage() : teamhome2_message(); return false; } callback?.(); return true; }; return { checkLogin }; }
组件内使用方式
// 组件顶层调用自定义Hook,符合Hook规则 const { checkLogin } = useLogin(); useEffect(() => { // 直接在useEffect中调用返回的普通校验函数即可 checkLogin(() => { checkauth(() => { checkteamexists(teamname); }) }) }, []);
方案2:自动校验登录(适合路由守卫场景)
如果校验逻辑是组件加载后自动执行,不需要手动控制时机,可以直接把校验逻辑放到Hook内部的useEffect中:
自定义Hook代码
import { useHistory, useEffect } from "react"; function useRequireLogin(isAuthenticated, redirectUrl = "/accounts/login", loginMessage) { const history = useHistory(); useEffect(() => { if (!isAuthenticated) { history.push(redirectUrl); loginMessage ? loginMessage() : teamhome2_message(); } }, [isAuthenticated, history, redirectUrl, loginMessage]); return isAuthenticated; }
组件内使用方式
// 组件顶层调用,自动校验登录状态 const isLogin = useRequireLogin(state.user.is_authenticated); useEffect(() => { // 登录状态有效再执行业务逻辑 if (!isLogin) return; checkauth(() => { checkteamexists(teamname); }) }, [isLogin]);
原写法错误原因
- 原自定义Hook直接在渲染流程中执行
history.push这种副作用操作,违反了React「渲染期间不能触发状态/路由更新」的规则,所以会抛出渲染期间更新的错误。 - 自定义Hook放到
useEffect内调用会打乱Hook的固定调用顺序,违反Hook的基本使用规则。
内容的提问来源于stack exchange,提问作者crimsonpython24
相关产品推荐
相关产品推荐

