2
import { TNewServicesProps } from '@/_constants/types';
import api from '@/_libs/api';
import { useMutation, useQueryClient } from '@tanstack/react-query';

const useManageServices = (
    method: 'create' | 'update' | 'delete',
    item_id?: number | string,
    values?: TNewServicesProps
) => {
    if ((method === 'update' || method === 'delete') && item_id == null) {
        throw new Error(`item_id is required for ${method} method`);
    }

    const queryClient = useQueryClient();
    const { data, error, isPending, mutateAsync, isSuccess } = useMutation({
        mutationKey: [`${method}_services`],
        mutationFn: async () => {
            switch (method) {
                case 'create':
                    return await api
                        .post(
                            `/admin/services/`,
                            JSON.stringify(values !== undefined ? values : {})
                        )
                        .then((res) => res.data)
                        .catch((error) => error.response.data);
                case 'update':
                    return await api
                        .patch(
                            `/admin/services/${item_id}/`,
                            JSON.stringify(values !== undefined ? values : {})
                        )
                        .then((res) => res.data)
                        .catch((error) => error.response.data);
                case 'delete':
                    return await api
                        .delete(`/admin/services/${item_id}/`)
                        .then((res) => res.data)
                        .catch((error) => error.response.data);
                default:
                    throw new Error(`Invalid method: ${method}`);
            }
        },
        //this is always fired, even when response code is 400, 500, etc.
        onSuccess: () => {
            queryClient.invalidateQueries({
                queryKey: ['services'],
            });
        },
                onError: () => {
                //should do something but never fired
                },
    });

    return { data, error, mutateStatus: mutateAsync, isPending, isSuccess };
};

export default useManageServices;

I'm currently experimenting with react-query in my project.

I'm facing an issue with error handling within my mutation.

When I check the network tab, I can see that the server responds with either a 400 or 500 status code, which I assumed would cause axios to throw an error, triggering the defined onError function.

However, the onSuccess function is always invoked.

What could I be overlooking? Thanks in advance.

0

2 Answers 2

2

I suspect the issue here is that your mutation function is catching the thrown errors before the useMutation hook can handle them to actually invoke any provided onError handler.

I suspect you could either not catch them at all, or simply re-throw them after they have been handled.

const { data, error, isPending, mutateAsync, isSuccess } = useMutation({
  mutationKey: [`${method}_services`],
  mutationFn: async () => {
    switch (method) {
      case 'create':
        return await api
          .post(
            "/admin/services/",
            JSON.stringify(values !== undefined ? values : {})
          )
          .then((res) => res.data);

       case 'update':
         return await api
           .patch(
             `/admin/services/${item_id}/`,
             JSON.stringify(values !== undefined ? values : {})
           )
           .then((res) => res.data);

       case 'delete':
         return await api
           .delete(`/admin/services/${item_id}/`)
           .then((res) => res.data);

       default:
         throw new Error(`Invalid method: ${method}`);
    }
  },
  onSuccess: () => {
    queryClient.invalidateQueries({
      queryKey: ['services'],
    });
  },
  onError: () => {
    // ...
  },
});
const { data, error, isPending, mutateAsync, isSuccess } = useMutation({
  mutationKey: [`${method}_services`],
  mutationFn: async () => {
    switch (method) {
      case 'create':
        return await api
          .post(
            "/admin/services/",
            JSON.stringify(values !== undefined ? values : {})
          )
          .then((res) => res.data)
          .catch((error) => {
            // error.response.data
            throw error;
          });

      case 'update':
        return await api
          .patch(
            `/admin/services/${item_id}/`,
            JSON.stringify(values !== undefined ? values : {})
          )
          .then((res) => res.data)
          .catch((error) => {
            // error.response.data
            throw error;
          });

      case 'delete':
        return await api
          .delete(`/admin/services/${item_id}/`)
          .then((res) => res.data)
          .catch((error) => {
            // error.response.data
            throw error;
          });

       default:
         throw new Error(`Invalid method: ${method}`);
    }
  },
  onSuccess: () => {
    queryClient.invalidateQueries({
      queryKey: ['services'],
    });
  },
  onError: () => {
    // ...
  },
});
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you @Drew Reese, I've removed the catch block and it works well.
btw, I have another question here, just thinking if you can also help me here stackoverflow.com/questions/78520883/…
@RonaldVergelDelaCruz That post appears to be asking for recommendations for a library or package that can handle social media postings which is off-topic as being subjective, or is asking how to implement it all manually... without any specific details, which is too broad and open-ended and doesn't fit our Q and A format well, e.g. asking a question about a specific issue or problem with an exact/specific expected outcome/goal. I think it needs quite a bit more focus and objectivity to be better received on this site.
0

Just like @Drew Reese's answer mentioned you're prematurely capturing the error when react query is already doing it. I think a simplified version of your code might be easier to see the issue.

mutationFn: async () => {
  const response = await api
    .delete(`/admin/services/${item_id}/`)
    .then((res) => { return res.data })
    .catch((error) => {
      // you're catching the error and returning the error response
      return error.response.data
    }
  return response; // this will be error.response.data, mutationFn doesn't fail, thus call the onSuccess function.
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.