skip to Main Content

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


  1. The main problem is that your save() function is async, but you do not await it. The consequence is that the sequence during onSave() will set the isSaving value back to false right away.

    Since there was no change in the next render (isSaving having been changed from false -> true -> false, no changes are to be observed on the isSaving value.

    The fix would be to await the save() function, or reset the variable after the await.

    Possible fix

    const onSave = async () => {
            setIsSaving(true);
            await save();
            setIsSaving(false);
        };
    

    Or

    const onSave = () => {
            setIsSaving(true);
            save();
        };
    
        const save = async () => {
            await mutation.mutate({ ...caseData, ...getValues() });
            setCaseFormValues(getValues());
            setIsSaving(false);
        };
    

    Or even

    const onSave = () => {
            setIsSaving(true);
            save().then(() => setIsSaving(false));
        };
    

    The same logic applied to the onSubmit().

    Login or Signup to reply.
  2. You’re using mutation.mutate along with await, but this is not an async function. You should be using mutation.mutateAsync that you can await, these are two different functions and only mutateAsync returns a Promise.

    When it’s used like this, setIsSaving(true) and setIsSaving(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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search