typescript - Tanstack React Query useMutation sempre verdadeiro em onSuccess


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;

Atualmente, estou a experimentar o react-query no meu projeto.

Estou a enfrentar um problema com o tratamento de erros dentro da minha mutação.

Quando verifico o separador de rede, posso ver que o servidor responde com um código de estado 400 ou 500, o que presumi que faria com que o axios lançasse um erro, acionando a função onError definida.

No entanto, a função onSuccess é sempre invocada.

O que é que eu poderia estar a ignorar? Obrigado antecipadamente.

Resposta #1

Suspeito que o problema aqui é que a sua função de mutação está a interceptar os erros lançados antes que o hook useMutation possa tratá-los para realmente invocar qualquer manipulador onError fornecido.

Suspeito que você poderia ou não os apanhar de todo, ou simplesmente relançá-los depois de terem sido tratados.

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: () => {
    // ...
  },
});

Resposta #2

Tal como a resposta do @Drew Reese mencionou, estás a capturar o erro prematuramente quando o react query já o está a fazer. Acho que uma versão simplificada do teu código pode tornar mais fácil ver o problema.

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.
}