如何在按钮点击时用useMutation结果更新token?
解决React Query useMutation获取数据undefined问题及优化方案
先排查data undefined的核心原因
你的mutateAsync返回data为undefined,大概率是下面几个问题:
- mutationFn未正确返回数据:如果只是发起请求但没return响应结果,自然拿不到数据;
- 响应结构处理错误:比如用Axios时,后端返回的数据在
response.data里,你直接return了response而非response.data; - 请求实际失败但未捕获:请求报错时
mutateAsync会抛出异常,若没加try/catch,代码不会走到获取data的逻辑,容易误以为是data为undefined。
正确实现代码示例
1. 基础版:修正mutationFn并使用onSuccess处理逻辑
import { useMutation, useState } from '@tanstack/react-query'; import axios from 'axios'; import { useAuthContext } from './AuthContext'; import { useNavigate } from 'react-router-dom'; const LoginComponent = () => { const { setToken } = useAuthContext(); const navigate = useNavigate(); const [code, setCode] = useState(''); const [login, setLogin] = useState(''); const loginMutation = useMutation({ // 核心:确保mutationFn返回后端的响应数据 mutationFn: async ({ code, login }) => { const response = await axios.post('/api/login', { code, login }); // 根据后端返回结构调整,比如直接返回response.data.token或整个数据对象 return response.data; }, // 成功回调:贴合React Query生命周期,统一处理后续逻辑 onSuccess: (data) => { setToken(data.token); navigate('/profile'); }, // 错误处理:捕获请求失败场景 onError: (error) => { console.error('登录失败:', error.response?.data?.message || error.message); } }); const handleLogin = async () => { try { await loginMutation.mutateAsync({ code, login }); } catch (error) { console.error('登录出错:', error); } }; return ( <div> <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="Code" /> <input value={login} onChange={(e) => setLogin(e.target.value)} placeholder="Login" /> {/* 结合loading状态优化用户体验 */} <button onClick={handleLogin} disabled={loginMutation.isLoading}> {loginMutation.isLoading ? '登录中...' : '登录'} </button> {/* 显示错误提示 */} {loginMutation.isError && ( <p style={{ color: 'red' }}> {loginMutation.error.response?.data?.message || '登录失败'} </p> )} </div> ); };
2. 优化版:抽离逻辑到自定义Hook和API层
第一步:抽离API请求逻辑(src/api/auth.js)
import axios from 'axios'; // 封装登录请求,统一处理响应结构 export const loginApi = async ({ code, login }) => { const response = await axios.post('/api/login', { code, login }); return response.data; };
第二步:创建自定义Hook(src/hooks/useLogin.js)
import { useMutation } from '@tanstack/react-query'; import { loginApi } from '../api/auth'; import { useAuthContext } from '../contexts/AuthContext'; import { useNavigate } from 'react-router-dom'; export const useLogin = () => { const { setToken } = useAuthContext(); const navigate = useNavigate(); return useMutation({ mutationFn: loginApi, onSuccess: (data) => { setToken(data.token); navigate('/profile'); }, onError: (error) => { console.error('登录失败:', error.response?.data?.message || error.message); // 可集成全局错误提示组件,比如react-toastify // toast.error(error.response?.data?.message || '登录失败'); } }); };
第三步:组件中使用自定义Hook
import { useState } from 'react'; import { useLogin } from './hooks/useLogin'; const LoginComponent = () => { const [code, setCode] = useState(''); const [login, setLogin] = useState(''); const loginMutation = useLogin(); const handleLogin = () => { loginMutation.mutate({ code, login }); }; return ( <div> <input value={code} onChange={(e) => setCode(e.target.value)} placeholder="Code" /> <input value={login} onChange={(e) => setLogin(e.target.value)} placeholder="Login" /> <button onClick={handleLogin} disabled={loginMutation.isLoading}> {loginMutation.isLoading ? '登录中...' : '登录'} </button> {loginMutation.isError && ( <p style={{ color: 'red' }}> {loginMutation.error.response?.data?.message || '登录失败'} </p> )} </div> ); };
关键优化点说明
- 逻辑复用:将API请求和mutation逻辑抽离,多个组件需要登录功能时可直接复用
useLoginHook; - 生命周期对齐:使用React Query的
onSuccess/onError回调处理结果,而非在mutateAsync后手动处理,更贴合框架状态管理逻辑; - 用户体验优化:结合
isLoading/isError状态提供即时反馈,比如禁用按钮、显示加载提示和错误文本; - 错误统一处理:集中捕获并处理请求错误,减少组件内冗余代码。
内容的提问来源于stack exchange,提问作者Bek
相关产品推荐
相关产品推荐

