React-query: mutation is returning undefined data - reactjs

Hello I am working with React-Query useMutation()
const { data: createTemplateData, mutate: createTemplate } = useMutation(
mutationService.createTemplate,
{
onSuccess: (newTemplate) => {
return queryClient.invalidateQueries("templates");
},
},
);
the function call looks like
const getMutationService = () => {
return {
createTemplate: async (
formData: CreateTemplateRequest,
): Promise<CreateTemplateResponse | undefined> => {
try {
const result = await templateApi.createTemplate(formData as any);
return result;
} catch (error) {
console.log("Error creating template", error);
}
},
};
};
I am able to mutate successfully (POST API Request) however, the data | createTemplateData is returning undefined after successful network request

Related

How to use PATCH method in react.js and typescript using react-query

I want to use the PATCH method to update a list of items instead of PUT. I have be able to remove any CORS blockers. The issue is the response i receive when i update is null and all the forms are then replaced.
task.service.ts
const update = async (id: any, { TaskName, TaskDescription, TaskOwner, TaskStatus, Skills, InitiativeLink, InitiativeName, TimeCommitment }: Task) => {
const response = await apiClient.patch<any>(`/tasks/${id}`, { TaskName, TaskDescription, TaskOwner, TaskStatus, Skills, InitiativeLink, InitiativeName, TimeCommitment });
return response.data;
};
OwnerHome.tsx
const { isLoading: isUpdatingTask, mutate: updateTask } = useMutation(
(putId: string) => {
return TaskService.update(
putId,
{
TaskName: putName,
InitiativeName: putInitiativeName,
TaskDescription: putDescription,
TaskOwner: putOwner,
InitiativeLink: putInitiativeLink,
Skills: putSkills,
TimeCommitment: putTimeCommitment,
TaskStatus: putStatus,
});
},
{
onSuccess: (res) => {
setPutResult(fortmatResponse(res));
},
onError: (err: any) => {
setPutResult(fortmatResponse(err.response?.data || err));
},
},
);
useEffect(() => {
if (isUpdatingTask) setGetResult('updating...');
}, [isUpdatingTask]);
function putData() {
if (selectedItems[0]) {
try {
updateTask(selectedItems[0].ID);
// setVisible(true);
} catch (err) {
setPutResult(fortmatResponse(err));
}
}
}

HTTP put and get(id) request ReactQuery

I change the redux in my project to ReactQuery,and i got some problem with put req in my code.
this is my code
const { dispatch } = store;
const editClientDataAsync = async ( id,data ) => {
await axiosObj().put(`clients/${id}`,data);
}
const { mutateAsync: editClientData, isLoading } = useMutation(['editClientData'], editClientDataAsync, {
onSuccess: () => dispatch({ type: SUCCESS_DATA, payload: { message: "Success" } }),
onError: () => dispatch({ type: ERROR_DATA, payload: { message: "Error" } })
});
return { editClientData, isLoading }
}
same problem with when i try to get some req with id
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async ({ queryKey }) => {
const [_, { id }] = queryKey;
console.log(queryKey)
if (!id)
return;
const { data } = await axiosObj().get(`clients/${id}`)
console.log(data)
return data;
}
const { data: clientDetails, isLoading } = useQuery(['ClientId', { id }], getClientDetails)
return { clientDetails, isLoading }
Mutation functions only take 1 argument
Check where you use the editClientData mutation and pass the arguments in one object.
const editClientDataAsync = async ({ id, data }) => {
await axiosObj().put(`clients/${id}`,data);
}
return useMutation(['editClientData'], editClientDataAsync, ...);
Are you sure you get an id passed to the function?
You can disable the query until you get that id with the enabled option, so you don't make an unnecessary http call.
const id = useSelector((state) => state?.clientData?.clientInfo?.data.id)
const getClientDetails = async (id) => {
const { data } = await axiosObj().get(`clients/${id}`)
return data;
}
return useQuery(['client', id], () => getClientDetails(id), { enabled: !!id })
Disable/pausing queries

React-query useInfiniteQuery: getNextPageParam not working

I'm stuck using useInfiniteQuery.
The first call works fine, but the next page is not called with getNextPageParam
const getProductItems = async (par) => {
console.log("axios :", par);
const res = await axios.get(`/api/v1/products`, {
params: par,
});
return {
result: res.data,
};
};
export default function useGetProductItems(params) {
const { data, isLoading, fetchNextPage, hasNextPage, isFetching } =
useInfiniteQuery(
["getItems"],
({ pars = params }) => getProductItems(pars),
{
getNextPageParam: (res) => {
console.log(res);
const nextParams = {
...res.result.pageInfo,
page: res.result.pageInfo.page + 1,
};
console.log("next :", nextParams);
return nextParams;
},
select: (data) => {
return data.pages[0].result.data;
},
}
);
return {
data,
isLoading,
fetchNextPage,
hasNextPage,
isFetching,
};
}
And the Query Client setting is like this
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
},
},
queryCache: new QueryCache({
onError: errorHandler,
}),
mutationCache: new MutationCache({
onError: errorHandler,
}),
});
As I am new to react-query, I am also wondering if there is any data that must be received from the API.
plz answer for me
You can access pageParam and send it as argument to your fetching function. Also it'd be a good idea to check if there really is a next page before incrementing the actual page number in getNextPageParam. Something like this:
const { data, isLoading, fetchNextPage, hasNextPage, isFetching } =
useInfiniteQuery(
['getItems'],
({ pageParam = 1 }) => getProductItems(pageParam), // pageParam defaults to the first page
{
getNextPageParam: lastPage => {
return lastPage.result.pageInfo.page < lastPage.result.pageInfo.totalPages // Here I'm assuming you have access to the total number of pages
? lastPage.result.pageInfo.page + 1
: undefined // If there is not a next page, getNextPageParam will return undefined and the hasNextPage boolean will be set to 'false'
},
select: data => {
return data.pages[0].result.data
},
}
)
I don't have information about how is your API endpoint built, but typically the request should look, for example, like this:
const getProductItems = async (page) => {
const res = await axios.get(`/api/v1/products?page=${page}`);
return {
result: res.data,
};
};

How to make a PATCH request in ReactJS ? (with Nestjs)

nestjs controller.ts
#Patch(':id')
async updateProduct(
#Param('id') addrId: string,
#Body('billingAddr') addrBilling: boolean,
#Body('shippingAddr') addrShipping: boolean,
) {
await this.addrService.updateProduct(addrId, addrBilling, addrShipping);
return null;
}
nestjs service.ts
async updateProduct(
addressId: string,
addrBilling: boolean,
addrShipping: boolean,
) {
const updatedProduct = await this.findAddress(addressId);
if (addrBilling) {
updatedProduct.billingAddr = addrBilling;
}
if (addrShipping) {
updatedProduct.shippingAddr = addrShipping;
}
updatedProduct.save();
}
there is no problem here. I can patch in localhost:8000/address/addressid in postman and change billingAddr to true or false.the backend is working properly.
how can i call react with axios?
page.js
const ChangeBillingAddress = async (param,param2) => {
try {
await authService.setBilling(param,param2).then(
() => {
window.location.reload();
},
(error) => {
console.log(error);
}
);
}
catch (err) {
console.log(err);
}
}
return....
<Button size='sm' variant={data.billingAddr === true ? ("outline-secondary") : ("info")} onClick={() => ChangeBillingAddress (data._id,data.billingAddr)}>
auth.service.js
const setBilling = async (param,param2) => {
let adressid = `${param}`;
const url = `http://localhost:8001/address/`+ adressid ;
return axios.patch(url,param, param2).then((response) => {
if (response.data.token) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
}
I have to make sure the parameters are the billlingddress field and change it to true.
I can't make any changes when react button click
Since patch method is working fine in postman, and server is also working fine, here's a tip for frontend debugging
Hard code url id and replace param with hard coded values too:
const setBilling = async (param,param2) => {
// let adressid = `${param}`;
const url = `http://localhost:8001/address/123`; // hard code a addressid
return axios.patch(url,param, param2).then((response) => { // hard code params too
console.log(response); // see console result
if (response.data.token) {
// localStorage.setItem("user", JSON.stringify(response.data));
}
// return response.data;
})
}
now it worked correctly
#Patch('/:id')
async updateProduct(
#Param('id') addrId: string,
#Body('billingAddr') addrBilling: boolean,
) {
await this.addrService.updateProduct(addrId, addrBilling);
return null;
}
const ChangeBillingAddress = async (param) => {
try {
await authService.setBilling(param,true).then(
() => {
window.location.reload();
},
(error) => {
console.log(error);
}
);
}
catch (err) {
console.log(err);
}
}
const setBilling= async (param,param2) => {
let id = `${param}`;
const url = `http://localhost:8001/address/`+ id;
return axios.patch(url,{billingAddr: param2}).then((response) => {
if (response.data.token) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
}

How can I trigger a GraphQL error to test an ErrorLink on Apollo Client?

I have the following ErrorLink set for Apollo Client.
export const errorLink = onError(
({ response, graphQLErrors, networkError, operation }: ErrorResponse) => {
notificationService.notify("An Error Occurred");
},
);
I need to test this implementation in a unit test.
I've the following to test Apollo Links
const MockQuery = gql`
query {
foo
}
`;
interface LinkResult<T> {
operation: Operation;
result: FetchResult<T>;
}
async function executeLink<T = ApolloLink>(
linkToTest: ApolloLink,
request: GraphQLRequest = { query: MockQuery },
) {
const linkResult = {} as LinkResult<T>;
return new Promise<LinkResult<T>>((resolve, reject) => {
execute(ApolloLink.from([linkToTest]), request).subscribe(
(result) => {
linkResult.result = result as FetchResult<T>;
},
(error) => {
reject(error);
},
() => {
resolve(linkResult);
},
);
});
}
it('triggers a notification on error', () => {
const testLink = new ApolloLink(() => {
await waitFor(() => expect(notificationSpy).toBeCalledWith('An Error Occurred'))
return null;
});
const link = ApolloLink.from([errorLink, testLink]);
executeLink(link);
});
These unit test work fine for other links like AuthLink where I test whether the auth token was set to the localStorage. But I cannot test the error link because I cannot trigger a GraphQL error.
You can create a mocked terminating link and provide a GraphQL operation result.
E.g.
errorLink.ts:
import { onError } from '#apollo/client/link/error';
type ErrorResponse = any;
export const errorLink = onError(({ response, graphQLErrors, networkError, operation }: ErrorResponse) => {
console.log('An Error Occurred');
console.log('graphQLErrors: ', graphQLErrors);
});
errorLink.test.ts:
import { ApolloLink, execute, Observable } from '#apollo/client';
import { gql } from 'apollo-server-express';
import { errorLink } from './errorLink';
const MockQuery = gql`
query {
foo
}
`;
describe('68629868', () => {
test('should pass', (done) => {
expect.assertions(1);
const mockLink = new ApolloLink((operation) =>
Observable.of({
errors: [
{
message: 'resolver blew up',
},
],
} as any),
);
const link = errorLink.concat(mockLink);
execute(link, { query: MockQuery }).subscribe((result) => {
expect(result.errors![0].message).toBe('resolver blew up');
done();
});
});
});
test result:
PASS apollo-graphql-tutorial src/stackoverflow/68629868/errorLink.test.ts (5.02s)
68629868
✓ should pass (14ms)
console.log src/stackoverflow/68629868/errorLink.ts:6
An Error Occurred
console.log src/stackoverflow/68629868/errorLink.ts:7
graphQLErrors: [ { message: 'resolver blew up' } ]
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 5.067s
package version: #apollo/client#3.3.20
I specifically needed to test handling NetworkError with TypeScript and it was a right pain to figure out, so here's how you can do it:
import {
ApolloLink,
execute,
FetchResult,
from,
gql,
GraphQLRequest,
Observable,
Operation,
} from '#apollo/client'
import { errorLink, notificationService } from './'
interface LinkResult<T> {
operation: Operation
result: FetchResult<T>
}
const MockQuery = gql`
query {
foo
}
`
class NetworkError extends Error {
bodyText
statusCode
result
message
response
constructor(networkErrorProps, ...params) {
super(...params)
const {
name,
bodyText,
statusCode,
result,
message,
response,
} = networkErrorProps
this.name = name
this.bodyText = bodyText
this.statusCode = statusCode
this.result = result
this.message = message
this.response = response
}
}
describe('errorLink', () => {
it('should handle error and send notification', async () => {
const mockLink = new ApolloLink((operation, forward) => {
let fetchResult: FetchResult = {
errors: [], // put GraphQLErrors here
data: null,
}
// Thanks https://stackoverflow.com/a/70936974/21217
let linkResult = Observable.of(fetchResult).map(_ => {
throw new NetworkError({
name: 'ServerParseError',
message: 'Unexpected token',
response: {},
bodyText: '<!DOCTYPE html><html><head></head><body>Error</body></html>',
statusCode: 503,
result: {},
})
})
return linkResult
})
async function executeLink<T = any, U = any>(
dataLink: ApolloLink
) {
const linkResult = {} as LinkResult<T>
return new Promise<LinkResult<T>>((resolve, reject) => {
execute(from([errorLink, dataLink]), {
query: MockQuery,
}).subscribe(
result => {
// We don't care
},
error => {
// We can resolve here to skip having a try / catch around the await below
resolve(linkResult)
},
)
})
}
const notificationSpy = jest.spyOn(notificationService, 'notify')
await executeLink(mockLink)
expect(notificationSpy).toHaveBeenCalledWith('An Error Occurred')
})
})

Resources