I have a parent component where I do api fetching with react query:
export default () => {
const { caseId } = useCaseProvider();
const { api } = useMockApi();
const { data: caseData, refetch, isRefetching } = api.getCasaData(caseId);
const mutation = api.postCaseData(caseId);
return (
<Suspense fallback={<Loader size="3xlarge" title="loading..." />}>
<CaseForm
caseData={caseData}
refetch={refetch}
isRefetching={isRefetching}
mutation={mutation}
/>
</Suspense>
);
};
And in the CaseForm component since I have button for both save and submit I need to set loading states for both of them depending on which one was clicked. I thought of setting flags with useState
hook for that:
const CaseForm = ({ caseData, refetch, isRefetching, mutation }) => {
const { caseFormValues, setCaseFormValues, setActiveStep } = useCaseProvider();
const initialValues = caseFormValues ?? createInitialValues(caseData);
const [isSaving, setIsSaving] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const onSave = async () => {
setIsSaving(true);
await save();
setIsSaving(false);
};
const save = async () => {
await mutation.mutate({ ...caseData, ...getValues() });
setCaseFormValues(getValues());
};
const onSubmit = async () => {
setIsSubmitting(true);
await save();
setIsSubmitting(false);
setActiveStep(STEPS[Stepper.INCOME]);
};
But, when I click on any of these buttons both isSubmitting and isSaving always remain false
. What am I doing wrong here, how can I fix this?
2
Answers
The main problem is that your
save()
function isasync
, but you do notawait
it. The consequence is that the sequence duringonSave()
will set theisSaving
value back to false right away.Since there was no change in the next render (
isSaving
having been changed fromfalse
->true
->false
, no changes are to be observed on theisSaving
value.The fix would be to
await
thesave()
function, or reset the variable after theawait
.Possible fix
Or
Or even
The same logic applied to the
onSubmit()
.You’re using
mutation.mutate
along withawait
, but this is not an async function. You should be usingmutation.mutateAsync
that you can await, these are two different functions and onlymutateAsync
returns a Promise.When it’s used like this,
setIsSaving(true)
andsetIsSaving(false)
is called in quick succession, either as a microtask or a next tick (not sure without checking) and you don’t see the result of the render-in between. Maybe react even manages to batch the updates?Sidenote:
The mutation itself has
mutation.isLoading
, so the ultimate question is why duplicate it yourself.