使用RTK Query编写React登录表单时遇Hook规则错误求助
解决RTK Query登录表单的两个错误
错误1:在handleSubmit中调用Hook违反规则
原因:React Hooks必须在函数组件顶层或自定义Hook里调用,不能在事件处理函数内部执行。useAuthenticateUserQuery是RTK Query生成的Hook,放在handleSubmit里不符合调用规则。
解决:登录是用户主动触发的操作,应该用Mutation替代Query。Query适合依赖参数自动触发的数据查询,Mutation更适合主动发起的验证/修改类请求。
错误2:isFetching未定义
原因:原代码里isFetching只在handleSubmit内部临时定义,组件顶层的按钮无法访问;再加上Hook调用位置错误,导致该值根本无法正确获取。
解决:改用Mutation后,在组件顶层调用Mutation Hook,直接拿到isFetching状态,供整个组件使用。
修改后的UserAPI.js
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; export const usersApi = createApi({ reducerPath: 'usersApi', baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:3005', prepareHeaders: (headers, { getState }) => { const token = getState().auth.token; if (token) { headers.set('authorization', `Bearer ${token}`); } return headers; }, }), endpoints: (builder) => ({ register: builder.mutation({ query: (user) => ({ url: '/users', method: 'POST', body: user, }), }), getUsers: builder.query({ query: () => '/users', }), // 将query改为mutation,适配主动触发的登录场景 authenticateUser: builder.mutation({ query: (credentials) => ({ url: '/users', method: 'GET', params: { email: credentials.email, password: credentials.password }, }), transformResponse: (response, { arg: credentials }) => { const users = response?.users; const matchingUser = users?.find((u) => u.email === credentials.email && u.password === credentials.password); return matchingUser || null; }, }), }), }); // 导出对应的Mutation Hook export const { useRegisterMutation, useGetUsersQuery, useAuthenticateUserMutation } = usersApi;
修改后的LoginForm.js
import { useState } from 'react'; import { useAuthenticateUserMutation } from '../store/apis/userApi'; import { useNavigate } from 'react-router-dom'; function LoginForm() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const navigate = useNavigate(); // 在组件顶层调用Mutation Hook,拿到触发函数和状态 const [authenticateUser, { isFetching }] = useAuthenticateUserMutation(); const handleSubmit = async (e) => { e.preventDefault(); // 调用mutation触发登录请求 const result = await authenticateUser({ email, password }); if (result.data) { // 登录成功跳转到 dashboard navigate('/dashboard'); } else { // 登录失败提示 alert('Invalid username or password'); } }; return ( <form onSubmit={handleSubmit} className="max-w-sm mx-auto"> <div className="mb-4"> <label htmlFor="email" className="block text-gray-700 font-bold mb-2"> Email </label> <input type="email" id="email" className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" value={email} onChange={(e) => setEmail(e.target.value)} /> </div> <div className="mb-6"> <label htmlFor="password" className="block text-gray-700 font-bold mb-2"> Password </label> <input type="password" id="password" className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" value={password} onChange={(e) => setPassword(e.target.value)} /> </div> <div className="flex items-center justify-between"> <button type="submit" disabled={!email || !password || isFetching} className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" > Login </button> </div> </form> ); } export default LoginForm;
补充说明:
- 改用Mutation后,请求由用户点击提交按钮主动触发,完全符合登录场景的交互逻辑
- 组件顶层获取的
isFetching可以直接控制按钮禁用状态,避免重复提交 - 调用
authenticateUser后返回的结果包含data和error,能更清晰地处理成功/失败分支
内容的提问来源于stack exchange,提问作者R9102
相关产品推荐
相关产品推荐

