DEV Community

Alan Pinhel
Alan Pinhel

Posted on

Usando Callbacks em Hooks Customizados do React

Tenho criado frequentemente hooks customizados, seja para reutilização de lógica ou extração de responsabilidade.

Há algum tempo, meu time deparou com o seguinte dilema: "como comunicar ao componente que utiliza um hook que um processo assíncrono terminou?".

Para entender melhor o cenário, veja o hook abaixo como exemplo:

function useApplyCoupon() {
  const [isApplying, setApplying] = useState(false);
  const { id } = useOrder();
  const { mutate } = useRemoteOrder();
  const { api } = useInfra();

  const applyCoupon = async (code: string) => {
    try {
      setApplying(true);
      await api.put(`.../orders/${id}/coupon`, { code });
      await mutate();
    } catch (error) {
      handleServerError(error);
    } finally {
      setApplying(false);
    }
  };

  return { applyCoupon, isApplying };
};
Enter fullscreen mode Exit fullscreen mode

A utilização fica da seguinte forma:

type FormValues = {
  code: string;
};

function ProductInformation() {
  const methods = useForm<FormValues>();
  const { applyCoupon, isApplying } = useApplyCoupon();

  const handleSubmit = ({ code }: FormValues) => {
    applyCoupon(code);
  };

  return (
    <form onSubmit={methods.handleSubmit(handleSubmit)}>
      <TextInput
        id="code"
        placeholder="Inserir cupom"
        error={methods.formState.errors.code?.message}
        size="xs"
        sx={{ flex: 1 }}
        {...methods.register('code', { required: true })}
      />
      <Button size="xs" type="submit" loading={isApplying}>
        Aplicar
      </Button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Agora, pense que após a ação de aplicar o cupom, queremos avisar o usuário se deu certo ou não.

Antes de eu compartilhar como meu time resolveu, deixem uma reação no post, pra eu saber se é um conteúdo relevante 🥺

Na época, a primeira tarefa com essa questão foi pega pelo Evandro, e ele resolveu de maneira muito simples, tornando-se um padrão nos nossos custom hooks, o que me motivou a compartilhar aqui.

Veja abaixo:

type Props = {
  onError?: () => void;
  onSuccess?: () => void;
};

function useApplyCoupon({ onError, onSuccess }: Props = {}) {
  const [isApplying, setApplying] = useState(false);
  const { id } = useOrder();
  const { mutate } = useRemoteOrder();
  const { api } = useInfra();

  const applyCoupon = async (code: string) => {
    try {
      setApplying(true);
      await api.put(`.../orders/${id}/coupon`, { code });
      await mutate();
      onSuccess?.();
    } catch (error) {
      if (axios.isAxiosError(error) && error.response?.status === 422) {
        onError?.();
        return;
      }
      handleServerError(error);
    } finally {
      setApplying(false);
    }
  };

  return { applyCoupon, isApplying };
};
Enter fullscreen mode Exit fullscreen mode

Utilização:

type FormValues = {
  code: string;
};

function ProductInformation() {
  const [isOpenModal, { open: openModal }] = useDisclosure();
  const methods = useForm<FormValues>();
  const { applyCoupon, isApplying } = useApplyCoupon({
    onSuccess() {
      openModal();
      confetti({
        origin: { y: 0.6 },
        particleCount: 100,
        spread: 70,
        zIndex: 2000,
      });
    },
    onError() {
      methods.setFocus('code');
      methods.setError('code', {
        type: 'manual',
        message: 'O código do cupom inserido não é válido. Tente outro.',
      });
    },
  });

  const handleSubmit = ({ code }: FormValues) => {
    applyCoupon(code);
  };

  return (
    <form onSubmit={methods.handleSubmit(handleSubmit)}>
      <TextInput
        id="code"
        placeholder="Inserir cupom"
        error={methods.formState.errors.code?.message}
        size="xs"
        sx={{ flex: 1 }}
        {...methods.register('code', { required: true })}
      />
      <Button size="xs" type="submit" loading={isApplying}>
        Aplicar
      </Button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

O que achou? Provavelmente existem outras formas de resolver, e eu gostaria de saber como você faria. Deixe nos comentários.

Top comments (1)

Collapse
 
tejota profile image
Olavo G. Junior

Que legal, mas vc malha?