如何在React登录组件中使用React/TanStack Query的useMutation?
用TanStack Query的useMutation重构登录组件
1. 确保已安装TanStack Query
如果还没安装,先执行安装命令:
npm install @tanstack/react-query # 或者用yarn yarn add @tanstack/react-query
2. 配置根组件的QueryClientProvider
在你的根组件(比如App.tsx或main.tsx)中配置TanStack Query的全局上下文,这是使用所有TanStack Query hooks的前提:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import Login from './path-to-your-login-component'; const queryClient = new QueryClient(); function App() { return ( <QueryClientProvider client={queryClient}> {/* 其他路由或组件 */} <Login /> </QueryClientProvider> ); } export default App;
3. 重构Login组件
下面是用useMutation替换原有逻辑后的完整代码,关键改动已标注:
import { useState, useContext } from 'react'; import { useNavigate } from 'react-router-dom'; import { useMutation } from '@tanstack/react-query'; import axios from 'axios'; import { UserContext } from './path-to-your-user-context'; // 定义登录响应的TypeScript类型,保证类型严谨性 type LoginResponse = { errors?: Array<{ msg: string }>; data: { user: { id: string; email: string; stripeCustomerId: string; }; token: string; }; }; const Login = () => { const navigate = useNavigate(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [state, setState] = useContext(UserContext); // 1. 抽离登录请求为独立函数 const loginUser = async (credentials: { email: string; password: string }) => { const { data } = await axios.post<LoginResponse>( "http://localhost:5001/auth/login", credentials ); return data; }; // 2. 使用useMutation处理登录逻辑 const { mutate, isLoading, error } = useMutation({ mutationFn: loginUser, // 登录成功后的回调 onSuccess: (response) => { if (response.errors?.length) { // 抛出后端返回的业务错误,让error状态捕获 throw new Error(response.errors[0].msg); } // 更新用户上下文 setState({ data: { id: response.data.user.id, email: response.data.user.email, stripeCustomerId: response.data.user.stripeCustomerId, }, loading: false, error: null, }); // 存储token并设置默认请求头 localStorage.setItem("token", response.data.token); axios.defaults.headers.common["authorization"] = `Bearer ${response.data.token}`; // 跳转到仪表盘 navigate("/dashboard"); }, // 登录失败的回调(包含请求失败和业务错误) onError: () => { // 可在这里添加自定义错误处理逻辑 }, }); // 3. 修改表单提交逻辑,调用mutation的mutate方法 const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); mutate({ email, password }); }; return ( <div className="login-card"> <div> <h3>Login</h3> </div> <form onSubmit={handleSubmit}> <div className="login-card-mb"> <label>Email</label> <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} disabled={isLoading} // 加载时禁用输入 /> </div> <div className="login-card-mb"> <label>Password</label> <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} disabled={isLoading} // 加载时禁用输入 /> </div> {/* 直接使用useMutation返回的error展示错误信息 */} {error && <p>{(error as Error).message}</p>} <button type="submit" disabled={isLoading}> {isLoading ? '登录中...' : 'Submit'} </button> </form> </div> ); }; export default Login;
关键改动说明
- 移除手动维护的
errorMsg:useMutation会自动捕获请求错误和业务错误,直接通过返回的error状态获取错误信息 - 自动管理加载状态:通过
isLoading状态控制按钮和输入框的禁用状态,无需手动维护loading的useState - 逻辑解耦:登录请求抽离为独立函数,组件逻辑更清晰,也方便后续复用
- 类型安全:通过
LoginResponse类型定义,确保后端返回数据类型符合预期,避免类型错误
内容的提问来源于stack exchange,提问作者ShrimpFrogPuffer
相关产品推荐
相关产品推荐

