如何在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
相关产品推荐
相关产品推荐

