React Query自定义hook错误状态未传递至调用组件问题
我在项目中使用React Query,在自定义hook里使用useMutation时遇到问题:所有查询类自定义hook都放在useApiData文件中,其中useUpdateCase的代码如下:
export const useUpdateCase = (caseId: number) => { const queryClient = useQueryClient(); const [error, setError] = useState(undefined); const mutation = useMutation({ mutationFn: (payload: UpdateCaseRequest): Promise<AxiosResponse<CaseDto>> => CASE_API.api.updateCase(caseId, payload), onSuccess: (data) => { queryClient.setQueryData(["case", caseId], data); console.log("onSuccess"); setError(undefined); }, onError: (error) => { console.log("onError", error); setError(error); }, }); console.log("error", error); console.log("mutation", mutation); return { mutation, error }; };
我通过Mock Service Worker返回503状态码模拟错误场景,MSW代码如下:
rest.put(`${environment.url.case}/api/case/:caseId`, async (req, res, ctx) => { const body = await req.json(); const existingCase = JSON.parse(localStorage.getItem(`case-${req.params.caseId}`)); const updatedCase = { ...existingCase, ...body }; const sucessHeaders = [ ctx.set("Content-Type", "application/json"), ctx.status(200), ctx.body(JSON.stringify(updatedCase)), ]; const errorHeaders = [ ctx.status(503), ctx.json({ errorMessage: "Service Unavailable", }), ]; const index = 1; //Math.random() < 0.9 ? 0 : 1; const response = [sucessHeaders, errorHeaders]; // if (index === 0) { // localStorage.setItem(`case-${req.params.caseId}`, JSON.stringify(updatedCase)); // } return res(...response[index]); }),
但在调用该自定义hook的组件中:
const updateCase = useUpdateCase(caseId); console.log("updateCase", updateCase);
发生错误时,updateCase并未更新,组件中无对应日志输出,但hook内已打印错误。控制台日志如下:
updateCase {mutation: {…}, error: undefined}
useApiData.tsx:48 error AxiosError {message: 'Request failed with status code 503', name: 'AxiosError', code: 'ERR_BAD_RESPONSE', config: {…}, request: XMLHttpRequest, …}
useApiData.tsx:49 mutation {context: undefined, data: undefined, error: AxiosError, failureCount: 1, failureReason: AxiosError, …}
请问哪里操作有误,为何更新后的错误状态无法传递至调用组件?
问题原因与解决方案
核心问题是你冗余维护了一个独立的useState错误状态,而React Query的useMutation本身已经内置了响应式的error状态,额外的状态不仅没必要,还会导致状态不同步、组件无法感知更新的问题。
修正步骤:
- 移除自定义hook中自己声明的
error状态及相关setError调用 - 直接返回
useMutation生成的mutation对象,组件从mutation中直接获取错误状态
修正后的useUpdateCase代码:
export const useUpdateCase = (caseId: number) => { const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (payload: UpdateCaseRequest): Promise<AxiosResponse<CaseDto>> => CASE_API.api.updateCase(caseId, payload), onSuccess: (data) => { queryClient.setQueryData(["case", caseId], data); console.log("onSuccess"); }, // 无需额外处理错误,useMutation会自动更新mutation.error }); console.log("mutation", mutation); return mutation; };
组件中调用方式:
// 直接获取mutation对象,或按需解构 const { mutate, error, isError } = useUpdateCase(caseId); // 可以在组件中通过useEffect监听错误变化,或者直接在JSX中使用 console.log("当前错误状态", error);
为什么原写法无效?
- 你自己维护的
error状态在更新后,虽然hook会重新执行,但组件若未显式依赖该状态(比如未在JSX中使用或通过useEffect监听),可能不会触发重新渲染,导致组件打印的仍是旧值 - React Query的
mutation对象本身是响应式的,当mutation状态(如error)变化时,useMutation会自动触发组件重新渲染,不需要额外维护状态 - 原代码中返回的
error和mutation.error可能存在闭包导致的不同步问题,属于冗余且易出错的写法
内容的提问来源于stack exchange,提问作者Leff

