react-query
react-query copied to clipboard
Type issue with useGenericMutation function.
Hello,
I am getting the following type issue within useGenericMutation function. Can you help me to understand what is needed to be done?
Thanks for the great tutorial.
Hi! Thank you, glad it was useful.
About this issue: it's strange that I didn't notice it before. I think the most logical way to fix it - change the types to this:
const useGenericMutation = <T, S>(
func: (data: T | S) => Promise<AxiosResponse<S>>,
url: string,
params?: object,
updater?: ((oldData: T, newData: S) => T) | undefined
) => {
const queryClient = useQueryClient();
return useMutation<AxiosResponse, AxiosError, T | S>(func, {
onMutate: async (data) => {
await queryClient.cancelQueries([url!, params]);
const previousData = queryClient.getQueryData([url!, params]);
queryClient.setQueryData<T>([url!, params], (oldData) => {
return updater ? updater(oldData!, data as S) : (data as T);
});
return previousData;
},
onError: (err, _, context) => {
queryClient.setQueryData([url!, params], context);
},
onSettled: () => {
queryClient.invalidateQueries([url!, params]);
},
});
};
The issue happened, because if there is no updater
function we should set (queryClient.setQueryData
) the data, which we received from the response. In our case we define data which we send as S
interface, and T
interface we define the data which we already have (and in some cases it could be the same values, that's why we are getting this issue).
For example:
- in case of POST request:
T
-JobInterface[]
,S
-JobInterface
(because existing data - the list of all jobs, the data which we want to add - just one new item of this list). The response will beT
(the actual list of objects). - in case of DELETE request:
T
-JobInterface[]
,S
-string | number
(existing data - is a list, and the data we send in the request -id
of deleting item). The response will beT
(the actual list of objects). - in case of PATCH request:
T
andS
will be the same, because the existing data (T
) isJobInterface
and the new data (S
) we want to change is theJobInterface
too. According this logic, for this casedata
inuseMutation
should be not justS
, butT | S
.
So, if data
could be T | S
, we should cast it manually. Actually if there is an updater - it means data
- S
, if there is no updater
- usually it means that T
and S
- are the same, that's why we could cast it to T
.
queryClient.setQueryData<T>([url!, params], (oldData) => {
return updater ? updater(oldData!, data as S) : (data as T);
});
I know it's a bit tricky, because we have different case and one generic function, but I think it will work. Sorry if the explanation is a messy. I made a fix comment.
Thanks for the explanation and the response! I will take my time to analyze and understand this
I want to add one new record, for that how to pass old data?