英文:
React query custom hook not propagating error state to component where it was called from
问题
我在项目中使用了react-query,但在使用useMutation
查询的自定义挂钩中遇到了一个错误。在这个自定义挂钩中,错误被正确记录,但在调用这个自定义挂钩的组件中,updateCase
在错误时没有被更新。在控制台中,日志如下所示:
> 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, …}
我在这里做错了什么?为什么更新后的值没有传播到调用它的组件中?
英文:
I use react-query in my project, and I got a bug in my custom hook where I use useMutation
query. I have all my custom hooks for queries
in the file useApiData
and useUpdateCase
is one of them:
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 };
};
Here in this custom hook, error is being logged properly, I am returning 503 with mock service worker:
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]);
}),
But, in the component where I am calling this custom hook from:
const updateCase = useUpdateCase(caseId);
console.log("updateCase", updateCase);
On error updateCase
is not updated here. Nothing is being logged while in the custom hook error is logged.
So, in my console, logs look like this:
> 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, …}
What am I doing wrong here, why is the updated value not being propagated to a component where it was called from?
答案1
得分: 1
以下是您要翻译的内容:
从查询函数文档中:
> 查询函数可以是任何返回承诺的函数。返回的承诺应该要么解析数据,要么抛出错误。
确保 api.updateCase()
抛出错误。
例如:
useUpdateCase.ts
:
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios, { AxiosResponse } from 'axios';
import { useState } from 'react';
type UpdateCaseRequest = any;
type CaseDto = any;
const api = {
updateCase(id, payload) {
return axios.put(`/api/case/${id}`, payload);
},
};
export const useUpdateCase = (caseId: number) => {
const queryClient = useQueryClient();
const [error, setError] = useState<any>(undefined);
const mutation = useMutation({
mutationFn: (payload: UpdateCaseRequest): Promise<AxiosResponse<CaseDto> | undefined> =>
api.updateCase(caseId, payload),
onSuccess: (data) => {
console.log('onSuccess');
queryClient.setQueryData(['case', caseId], data);
setError(undefined);
},
onError: (error) => {
console.log('onError');
setError(error);
},
});
return { mutation, error };
};
useUpdateCase.test.tsx
:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook } from '@testing-library/react-hooks';
import axios from 'axios';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import React from 'react';
import { useUpdateCase } from './useUpdateCase';
const server = setupServer(
rest.put(`/api/case/:caseId`, async (req, res, ctx) => {
const sucessHeaders = [ctx.set('Content-Type', 'application/json'), ctx.status(200), ctx.body(JSON.stringify({}))];
const errorHeaders = [
ctx.status(503),
ctx.json({
errorMessage: 'Service Unavailable',
}),
];
const index = 1;
const response = [sucessHeaders, errorHeaders];
return res(...response[index]);
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('67902700', () => {
test('loads and displays greeting', async () => {
const queryClient = new QueryClient({
logger: {
log: console.log,
warn: console.warn,
error: process.env.NODE_ENV === 'test' ? () => {} : console.error,
},
defaultOptions: {
mutations: {
retry: false,
cacheTime: Infinity,
},
},
});
const { result, waitForNextUpdate } = renderHook(() => useUpdateCase(1), {
wrapper: ({ children }) => <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>,
});
result.current.mutation.mutate({ text: 'new text' });
await waitForNextUpdate();
expect(axios.isAxiosError(result.current.error)).toBeTrue();
expect(result.current.error.response.data.errorMessage).toEqual('Service Unavailable');
});
});
测试结果:
PASS stackoverflow/76033596/useUpdateCase.test.tsx
67902700
✓ loads and displays greeting (81 ms)
console.log
onError
at Object.onError (stackoverflow/76033596/useUpdateCase.ts:27:15)
------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All files | 82.35 | 100 | 80 | 81.25 |
useUpdateCase.ts | 82.35 | 100 | 80 | 81.25 | 22-24
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.456 s, estimated 9 s
Ran all test suites related to changed files.
包版本:
"@tanstack/react-query": "^4.10.3",
"msw": "^0.29.0",
"react": "^16.14.0",
"@testing-library/react-hooks": "^8.0.1",
"axios": "^0.21.1",
英文:
From the query functions docs:
> A query function can be literally any function that returns a promise. The promise that is returned should either resolve the data or throw an error.
Make sure the api.updateCase()
throws an error.
E.g.
useUpdateCase.ts
:
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios, { AxiosResponse } from 'axios';
import { useState } from 'react';
type UpdateCaseRequest = any;
type CaseDto = any;
const api = {
updateCase(id, payload) {
return axios.put(`/api/case/${id}`, payload);
},
};
export const useUpdateCase = (caseId: number) => {
const queryClient = useQueryClient();
const [error, setError] = useState<any>(undefined);
const mutation = useMutation({
mutationFn: (payload: UpdateCaseRequest): Promise<AxiosResponse<CaseDto> | undefined> =>
api.updateCase(caseId, payload),
onSuccess: (data) => {
console.log('onSuccess');
queryClient.setQueryData(['case', caseId], data);
setError(undefined);
},
onError: (error) => {
console.log('onError');
setError(error);
},
});
return { mutation, error };
};
useUpdateCase.test.tsx
:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook } from '@testing-library/react-hooks';
import axios from 'axios';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import React from 'react';
import { useUpdateCase } from './useUpdateCase';
const server = setupServer(
rest.put(`/api/case/:caseId`, async (req, res, ctx) => {
const sucessHeaders = [ctx.set('Content-Type', 'application/json'), ctx.status(200), ctx.body(JSON.stringify({}))];
const errorHeaders = [
ctx.status(503),
ctx.json({
errorMessage: 'Service Unavailable',
}),
];
const index = 1;
const response = [sucessHeaders, errorHeaders];
return res(...response[index]);
})
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
describe('67902700', () => {
test('loads and displays greeting', async () => {
const queryClient = new QueryClient({
logger: {
log: console.log,
warn: console.warn,
error: process.env.NODE_ENV === 'test' ? () => {} : console.error,
},
defaultOptions: {
mutations: {
retry: false,
cacheTime: Infinity,
},
},
});
const { result, waitForNextUpdate } = renderHook(() => useUpdateCase(1), {
wrapper: ({ children }) => <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>,
});
result.current.mutation.mutate({ text: 'new text' });
await waitForNextUpdate();
expect(axios.isAxiosError(result.current.error)).toBeTrue();
expect(result.current.error.response.data.errorMessage).toEqual('Service Unavailable');
});
});
Test result:
PASS stackoverflow/76033596/useUpdateCase.test.tsx
67902700
✓ loads and displays greeting (81 ms)
console.log
onError
at Object.onError (stackoverflow/76033596/useUpdateCase.ts:27:15)
------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All files | 82.35 | 100 | 80 | 81.25 |
useUpdateCase.ts | 82.35 | 100 | 80 | 81.25 | 22-24
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.456 s, estimated 9 s
Ran all test suites related to changed files.
package versions:
"@tanstack/react-query": "^4.10.3",
"msw": "^0.29.0",
"react": "^16.14.0",
"@testing-library/react-hooks": "^8.0.1",
"axios": "^0.21.1",
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论