1

I have a component where I am fetching data with react-query and setting initial form values:

const { data: caseData, refetch, isRefetching } = useQuery({
  queryKey: `caseData-${caseId}`,
  queryFn: () => fetchCaseById(caseId),
  staleTime: Infinity,
});

<Suspense fallback="Loading">
    <CaseForm initialValues={caseData} refetch={refetch}/>
</Suspense>

Inside a child CaseForm component I have a button that onClick triggers refetch function:

<Button
    type="button"
    loading={isRefetching}
    variant="secondary"
    onClick={() => refetch()}
    className="w-max"
    size="small"
>
    Reload
</Button>

I would like to only create initial values after data is fetched the first time on mount and later on refetch. I thought of lifting form values into context provider so that i can check state there. Something like this:

const { data: caseData, refetch, isRefetching } = api.getCaseData(caseId);

<Suspense fallback="Loading">
    <CaseForm initialValues={caseData} refetch={refetch}/>
</Suspense>

And then in CaseForm:

const { caseFormValues, setCaseFormValues } = useCaseProvider();

useEffect(() => {
  if (!caseFormValues) setCaseFormValues(props.initialValues);
  
  return () => {
      setCaseFormValues(getValues());
  };
}, []);

const {
  handleSubmit,
  control,
  reset,
  formState: { errors },
} = useForm({
  defaultValues: caseFormValues ? caseFormValues : props.initialValues,
});

useEffect(() => {
   if (!props.isRefetching) {
      reset(props.initialValues);
   }
}, [props.initialValues, props.isRefetching]);

This works fine when I fetch data for the first time, it also works fine when I click and refetch data. But, if the component is unmounted and then remounted again, so if I move to another page and go back to form again, useEffect with dependencies props.initialValues, isRefetching is being called, even though they haven't changed. That resets the form values when I don't want that. I have logged the values of the props and I can see that they are the same, yet useEffect is called every time the component remounts. Why is that, what I am doing wrong here?

const { caseFormValues, setCaseFormValues } = useCaseProvider();

// dependencies log the same value
console.log('initialValues', props.initialValues)
console.log('isFetching', props.isFetching)

useEffect(() => {
  if (!caseFormValues) setCaseFormValues(props.initialValues);
  
  return () => {
      setCaseFormValues(getValues());
  };
}, []);

const {
  handleSubmit,
  control,
  reset,
  formState: { errors },
} = useForm({
  defaultValues: caseFormValues ? caseFormValues : props.initialValues,
});

useEffect(() => {
   console.log('called')
   if (!props.isRefetching) {
      reset(props.initialValues);
   }
}, [props.initialValues, props.isRefetching]);
Leff
  • 1,968
  • 24
  • 97
  • 201
  • This may not answer the question but personally I'd only trigger a refetch on a specific scenario rather than remount. Like the user click something, or another component does something. There are any number of things that can trigger a remount which your problem above demonstrates. – Steve Tomlin Feb 27 '23 at 08:55
  • I am only triggering refetch on click, the problem is that I would like to know when data was fetched after I clicked for refetch. And I thought of using the ```useEffect``` hook for that to track when it ```isRefetching``` and ```initialValues``` were changed. – Leff Feb 27 '23 at 08:58
  • why not put else if(props.initialValues) {// debug stuff here..} if your if statement? – Steve Tomlin Feb 27 '23 at 09:06

1 Answers1

0

Have a look at this thread: Set state with same value using hooks will cause a rerender?

The values may be the same, but for React its a "new" object holding these values. Thus your useEffect will run, even if you know that the values inside your dependencies should not have changed. To prevent executing the content of your useEffect, you can compare the props of the previous render cycle and the current render cycle inside your useEffect with some kind of comparison method, and then decide if you want to call reset() or not.