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

如何在useMutation的onError中捕获Axios拦截器返回的错误?

问题

在React项目中使用React Query的useMutation处理用户注册请求时遇到以下问题:

  • 服务端返回Email id already exists错误时,Axios拦截器已正确捕获并抛出错误,useMutation的onError也能拿到AxiosError对象,但无法在signup.jsx的Formik onSubmit中及时获取错误信息
  • 调用mutate(values)后,首次点击提交按钮时registerMutate?.isError不触发,第二次点击才会触发,使用mutateAsync结果相同

现有代码如下:

Axios拦截器

axiosInstance.interceptors.response.use((response) => {
    console.log('RESPONSE CALLED')
    console.log(response)
    return response;
}, (error) => {
    console.log('INTERCEPTOR RESPONSE ERROR')
    return Promise.reject(error);
})

apiAuth.js

async function AddUser(user) {
    try {
        const response = await axios.post("/Auth/register", user)
        console.log(response)
        return response
    } catch (error) {
        console.log('Register call error')
        console.log(error.response.status)    // 返回451
        console.log(error.response.data)      // 返回Email ID already exists
        return await Promise.reject(error)
    }
}

export const apiAuthRegister = () => {
  return (
    useMutation({
        mutationKey: ['authRegister'],
        mutationFn:  (user) =>  AddUser(user),
        onSuccess: (data) => {
            console.log('REGISTERED SUCCESSFULLY')
        },
        onError: (error) => {
            console.log('REGISTER FAILED')
            console.log(error)   // 成功打印AxiosError对象
            return error.response.data   // 如何在signup.jsx的onSubmit中拿到这个错误信息?
        }
    })
  )
}

signup.jsx

const SignUp = () => {  
    const [errorMsg, setErrorMsg] = useState(initialValue)
    useEffect(()=>{
        console.log('USE EFFECT CALLED')
    },[errorMsg])
    const registerMutate = apiAuthRegister();
    return(
        <Formik
            onSubmit={  (values, formik) => {
              console.log("On Submit Called");
              registerMutate.mutate(values)
              if(registerMutate?.isLoading) return console.log('loading...') // 未触发
              if(registerMutate?.isError){   // 首次点击提交按钮时不触发
                  const newErrorMsg = {...errorMsg}
                  const data = registerMutate?.error.response.data
                  newErrorMsg.emailID = data
                  setErrorMsg(newErrorMsg)
                  console.log(errorMsg)
                  formik.setErrors({
                    emailID:errorMsg.emailID
                  });
                  console.log(registerMutate?.error?.response?.data) // 第二次提交才打印
              } 
              if(registerMutate?.isSuccess) return console.log('success...'+ registerMutate.data)  // 未触发
            }}
        >
        //... 表单元素
        </Formik>
    )
}  

解决方案

核心原因

useMutation的状态更新(isLoading/isError/isSuccess)是异步的,调用mutate后立刻同步判断状态,此时状态还未更新,所以首次点击时拿不到错误。


方案1:使用mutateAsync结合async/await在onSubmit中直接处理

修改signup.jsx的onSubmit为异步函数,用try/catch包裹mutateAsync,直接捕获错误并更新表单:

const SignUp = () => {  
    const [errorMsg, setErrorMsg] = useState(initialValue)
    const registerMutate = apiAuthRegister();
    
    return(
        <Formik
            onSubmit={ async (values, formik) => {
              console.log("On Submit Called");
              try {
                await registerMutate.mutateAsync(values);
                // 注册成功逻辑
                console.log('success...');
              } catch (error) {
                // 直接捕获错误
                const errorData = error.response.data;
                const newErrorMsg = {...errorMsg, emailID: errorData};
                setErrorMsg(newErrorMsg);
                formik.setErrors({ emailID: errorData });
                console.log(errorData);
              }
            }}
        >
        //... 表单元素
        </Formik>
    )
}  

方案2:通过useEffect监听useMutation的状态变化

利用React的useEffect监听registerMutate.isError和registerMutate.error的变化,当错误发生时自动更新表单:

const SignUp = () => {  
    const [errorMsg, setErrorMsg] = useState(initialValue)
    const registerMutate = apiAuthRegister();
    
    // 监听错误状态变化
    useEffect(() => {
        if (registerMutate.isError && registerMutate.error) {
            const errorData = registerMutate.error.response.data;
            const newErrorMsg = {...errorMsg, emailID: errorData};
            setErrorMsg(newErrorMsg);
        }
    }, [registerMutate.isError, registerMutate.error, errorMsg]);
    
    return(
        <Formik
            onSubmit={ (values, formik) => {
              console.log("On Submit Called");
              // 重置之前的错误
              formik.setErrors({});
              setErrorMsg(initialValue);
              registerMutate.mutate(values);
            }}
        >
        {({ setErrors }) => {
            // 内部监听errorMsg变化更新Formik错误
            useEffect(() => {
                if (errorMsg.emailID) {
                    setErrors({ emailID: errorMsg.emailID });
                }
            }, [errorMsg, setErrors]);
            
            return (
                //... 表单元素
            )
        }}
        </Formik>
    )
}  

优化apiAuth.js的代码

AddUser函数中不需要return await Promise.reject(error),直接throw error即可,useMutation会自动捕获错误:

async function AddUser(user) {
    try {
        const response = await axios.post("/Auth/register", user)
        console.log(response)
        return response
    } catch (error) {
        console.log('Register call error')
        console.log(error.response.status)
        console.log(error.response.data)
        throw error; // 直接抛出错误,useMutation会处理
    }
}

export const apiAuthRegister = () => {
  return useMutation({
        mutationKey: ['authRegister'],
        mutationFn: (user) => AddUser(user),
        onSuccess: (data) => {
            console.log('REGISTERED SUCCESSFULLY')
        },
        onError: (error) => {
            console.log('REGISTER FAILED')
            console.log(error)
            // 这里不需要返回错误,错误会被mutateAsync或者状态捕获
        }
    })
}

内容的提问来源于stack exchange,提问作者Liam neesan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 16:27:25