Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type issue with useGenericMutation function. #1

Open
berkerdemirer opened this issue Apr 9, 2022 · 3 comments
Open

Type issue with useGenericMutation function. #1

berkerdemirer opened this issue Apr 9, 2022 · 3 comments

Comments

@berkerdemirer
Copy link

Hello,

I am getting the following type issue within useGenericMutation function. Can you help me to understand what is needed to be done?

image

Thanks for the great tutorial.

horprogs added a commit that referenced this issue Apr 10, 2022
@horprogs
Copy link
Owner

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 be T (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 be T (the actual list of objects).
  • in case of PATCH request: T and S will be the same, because the existing data (T) is JobInterface and the new data (S) we want to change is the JobInterface too. According this logic, for this case data in useMutation should be not just S, but T | 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.

@berkerdemirer
Copy link
Author

Thanks for the explanation and the response! I will take my time to analyze and understand this

@codingwithashu
Copy link

I want to add one new record, for that how to pass old data?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants