React表单调用GraphQL登录接口遇Hook错误及Apollo异常求助
问题分析与解决方案
1. 核心错误:误用useQuery处理登录请求
登录属于提交凭证并获取授权令牌的操作,对应GraphQL的Mutation类型,而非用于查询数据的Query类型。你用urql的useQuery来处理登录是根本问题:
useQuery会在组件挂载时自动发起请求,导致页面刷新后首次触发空请求useQuery默认缓存请求结果,所以更换账号密码后仍发送旧数据- 若你在按钮点击的事件回调里直接调用自定义hook,会违反React Hook调用规则,触发
Invalid hook call错误
2. 修复Invalid hook call错误
确保你的自定义hook严格遵循Hook规则:
- 只能在React组件的顶层或其他自定义Hook的顶层调用,不能在事件处理函数、条件判断、循环、嵌套函数内调用
- 自定义Hook必须以
use开头(这一点你已经符合要求)
3. 正确实现登录逻辑(基于urql)
步骤1:定义登录Mutation
mutation Login($email: String!, $password: String!) { login(email: $email, password: $password) { token } }
步骤2:创建useLogin自定义Hook(改用useMutation)
import { useMutation } from 'urql'; import Swal from 'sweetalert2'; import { useNavigate } from 'react-router-dom'; export const useLogin = () => { const navigate = useNavigate(); const [, loginMutation] = useMutation(` mutation Login($email: String!, $password: String!) { login(email: $email, password: $password) { token } } `); const handleLogin = async (email, password) => { try { const result = await loginMutation({ email, password }); if (result.error) { Swal.fire('错误', '账号或密码错误', 'error'); return; } const { token } = result.data.login; localStorage.setItem('authToken', token); navigate('/'); // 跳转到主页 } catch (err) { Swal.fire('错误', '登录失败,请稍后重试', 'error'); } }; return { handleLogin }; };
步骤3:在登录组件中使用Hook
import { useState } from 'react'; import { useLogin } from './hooks/useLogin'; const LoginForm = () => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const { handleLogin } = useLogin(); const onSubmit = (e) => { e.preventDefault(); handleLogin(email, password); }; return ( <form onSubmit={onSubmit}> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="邮箱" required /> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} placeholder="密码" required /> <button type="submit">Login</button> </form> ); }; export default LoginForm;
4. 避免旧问题的关键要点
- 用
useMutation替代useQuery:mutation不会自动缓存结果,每次调用都会发送最新的变量值 - 将登录逻辑封装在
handleLogin函数中,仅在表单提交时触发,避免组件挂载时自动发起请求 - 直接传递实时的
email和password状态作为mutation变量,确保每次请求都是最新输入
最佳实践建议
- 给urql客户端配置认证拦截器,让后续请求自动携带localStorage中的token
- 添加登录加载状态:在
handleLogin中设置loading标记,禁用提交按钮避免重复请求 - 处理token过期逻辑:后续请求捕获401错误时,清除localStorage并跳转回登录页
内容的提问来源于stack exchange,提问作者José
相关产品推荐
相关产品推荐

