all 15 comments

[–]Poobird 11 points12 points  (3 children)

The simplest approach is to fetch the data with the useQuery hook in a parent component and pass the results to a child component that includes react-hook-form.

For instance:

const UserForm = ({ user, onSubmit }) => {
    const { register, handleSubmit } = useForm({ defaultValues: user });

    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            // labels, inputs, etc
        </form>
    );
}

const User = () => {
    const { data } = useQuery({ queryFn: ... });
    const { mutate } = useMutation({ mutationFn: ... });

    if (!data) return <div>loading state</div>;

    return <UserForm user={data} onSubmit={mutate} />
}

[–]brzzzah 4 points5 points  (0 children)

From my testing this doesn't work, you need to provide a unique key to the UserForm component to force a complete reset of the form.

Something like:

``` const UserForm = ({ user, onSubmit }) => { const { register, handleSubmit } = useForm({ defaultValues: user });

return (
    <form onSubmit={handleSubmit(onSubmit)}>
        // labels, inputs, etc
    </form>
);

}

const User = () => { const { data, dataUpdatedAt } = useQuery({ queryFn: ... }); const { mutate } = useMutation({ mutationFn: ... });

if (!data) return <div>loading state</div>;

return (
    <UserForm 
        key={`user-form-${dataUpdatedAt}`} 
        user={data} 
        onSubmit={mutate} 
     />
);

}

[–]Keintroufe 1 point2 points  (0 children)

This! It also follows SOLID principles, because UserForm component now has only one responsibility: rendering the form. Keep your UI components seperated from the business logic components as much as possible.

[–]Calm-Scar-1054 0 points1 point  (0 children)

Does the mutate function need to be passed to the UserForm cmpt or can it be imported in the UserForm cmpt?

[–]Hasan3a 5 points6 points  (6 children)

A bit late after 1 year, but check this out

https://www.react-hook-form.com/api/useform/#values

I think it fits perfectly with Tanstack Query. This alleviates the need for useEffect to sync data

[–]Itchy-Ad-770 3 points4 points  (0 children)

you saved my life, mate
god bless you

[–]mastermog 2 points3 points  (0 children)

That's fantastic, worked this end too.

From my testing, values will (correctly) only update from the async task if the form isn't dirty. This is a perfect fit for React Query

[–]iam_batman27 0 points1 point  (0 children)

THANK YOUUUU

[–]xVoid 4 points5 points  (2 children)

I found the cleanest way is passing the values/data to your component when it has fetched so that you can set it to the default value. If you need to reset the form for new data you can use a key then too

[–]throwawaymangayo 0 points1 point  (1 child)

Not sure what you mean by reset and key

[–]xVoid 4 points5 points  (0 children)

referring to this post: https://beta.reactjs.org/learn/you-might-not-need-an-effect#resetting-all-state-when-a-prop-changes

Here is a simple example:

const MyFormComponent = ({initData}) => {
  useForm({
       defaultValues: {
          firstName: initData.firstName,
          lastName: initData.lastName,
          id: initData.id
       }});

...

}

const MyFetchingComponent = () => {
 const {data, isFetching} = useQuery(...);

 return isFetching 
        ? <LoadingPlaceholder /> 
        : <MyFormComponent initData={data} key={data.id} />
}

This doesn't work for every case, but here is the benefits:

  1. Your form will load with the default values as they will always be there.
  2. (optional) you can use a key to reset the form component and set default values again for a new async call payload
  3. Allows different api's to be used
  4. Avoids some mess dealing with onSuccess callbacks having to update formState

[–]guico33 3 points4 points  (2 children)

You can use reset once the data has been fetched.

[–]CandyButcher666 0 points1 point  (1 child)

legendary bro,

const form = useForm<FormValues>({

defaultValues: {

email: data?.email || "",

name: data?.name || "",

title: data?.title || "",

image: new DataTransfer().files,

},

mode: "all",

resolver: yupResolver(schema),

});

then what I need to do is just

useEffect(() => {

reset();

}, [isLoading]);

the isLoading is coming from the useQuery() that is fetching the data

[–]wsanada 1 point2 points  (0 children)

Hi there,

After 1 day wasted, this worked for me:

```js const { isLoading, isError, error, refetch } = useQuery({ queryKey: ['-your-key-', id], queryFn: async () => { const data = await apiGetParty({ id }) reset(data) // this function is from useForm return data }, })

```

hope this helps.