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 };
};
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>
);
}
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 };
};
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>
);
}
O que achou? Provavelmente existem outras formas de resolver, e eu gostaria de saber como você faria. Deixe nos comentários.
Top comments (2)
Que legal, mas vc malha?
Valeu! Eu treino 💪😂