React Invalid hook call错误排查及AuthService改造方案咨询
解决React钩子调用错误的方案
问题原因
你在AuthService.js的普通函数AuthHandleLogin里直接调用了useContext钩子,违反了React钩子核心规则——钩子只能在函数组件的顶层或自定义钩子函数的顶层调用,不能在普通函数、循环、条件判断内调用。
方案一:将权限逻辑封装为自定义钩子
自定义钩子命名必须以use开头,React会识别这是合法的钩子调用,适合复用有状态的逻辑。
修改AuthService.js:
import { useContext } from "react"; import { AppContext } from '../pages/app'; // 自定义钩子,统一封装权限操作 export const useAuth = () => { const { setUserAuth } = useContext(AppContext); const handleLogin = (response) => { const auth_data = { token: response.access_token }; setUserAuth(auth_data); sessionStorage.setItem("auth", JSON.stringify(auth_data)); }; const handleLogout = () => { // 后续登出逻辑示例 setUserAuth(null); sessionStorage.removeItem("auth"); }; return { handleLogin, handleLogout }; };
改造后的Login.js使用自定义钩子:
import { useAuth } from '../../utils/AuthService'; import api from '../../utils/api'; const Login = () => { // 在组件顶层调用自定义钩子 const { handleLogin: authHandleLogin } = useAuth(); function handleLogin(e) { e.preventDefault(); api.userLogin(username, password, handleLoginResult); } function handleLoginResult(response, success) { if (!success) { alert(response); } else { authHandleLogin(response); } } return ( <section id='login'> <div className='flex flex-col items-center'> <form onSubmit={handleLogin}> </form> </div> </section> ) } export default Login;
方案二:将依赖参数传入服务函数
如果不想用自定义钩子,可以把组件中获取的setUserAuth作为参数传给AuthHandleLogin,避免在服务函数中调用钩子。
修改AuthService.js:
// 无需导入React钩子和上下文 export const AuthHandleLogin = (response, setUserAuth) => { const auth_data = { token: response.access_token }; setUserAuth(auth_data); sessionStorage.setItem("auth", JSON.stringify(auth_data)); }; export const AuthHandleLogout = (setUserAuth) => { // 后续登出逻辑示例 setUserAuth(null); sessionStorage.removeItem("auth"); };
改造后的Login.js传入依赖参数:
import { AuthHandleLogin } from '../../utils/AuthService'; import { useContext } from "react"; import { AppContext } from '../pages/app'; import api from '../../utils/api'; const Login = () => { // 在组件顶层获取上下文方法 const { setUserAuth } = useContext(AppContext); function handleLogin(e) { e.preventDefault(); api.userLogin(username, password, handleLoginResult); } function handleLoginResult(response, success) { if (!success) { alert(response); } else { // 传入setUserAuth参数 AuthHandleLogin(response, setUserAuth); } } return ( <section id='login'> <div className='flex flex-col items-center'> <form onSubmit={handleLogin}> </form> </div> </section> ) } export default Login;
关键提示
- 原代码中
handleLoginResult回调里调用useContext其实也违反钩子规则,只是未触发错误,后续可能引发异常,必须把钩子调用移到组件顶层。 - 自定义钩子是React推荐的复用状态逻辑的方式,更适合后续扩展登出、权限校验等更多功能。
内容的提问来源于stack exchange,提问作者Parker
相关产品推荐
相关产品推荐

