Usando Callbacks em Hooks Customizados do React
Alan Pinhel
Posted on March 4, 2024
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.
Posted on March 4, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 18, 2024