You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:35:20