176

I am creating a page for user to update personal data with React-Hook-Form. Once paged is loaded, I use useEffect to fetch the user's current personal data and set them into default value of the form.

I put the fetched value into defaultValue of <Controller />. However, it is just not showing in the text box. Here is my code:

import React, {useState, useEffect, useCallback} from 'react';
import { useForm, Controller } from 'react-hook-form'
import { URL } from '../constants';
const UpdateUserData = props => {
 const [userData, setUserData] = useState(null);
 const { handleSubmit, control} = useForm({mode: 'onBlur'});
 const fetchUserData = useCallback(async account => {
 const userData = await fetch(`${URL}/user/${account}`)
 .then(res=> res.json());
 console.log(userData);
 setUserData(userData);
 }, []);
 useEffect(() => {
 const account = localStorage.getItem('account');
 fetchUserData(account);
 }, [fetchUserData])
 const onSubmit = async (data) => {
 // TODO
 }
 return (
 <div>
 <form onSubmit={handleSubmit(onSubmit)}>
 <div>
 <label>User Name:</label>
 <Controller
 as={<input type='text' />}
 control={control}
 defaultValue={userData ? userData.name : ''}
 name='name'
 />
 </div>
 
 <div>
 <label>Phone:</label>
 <Controller
 as={<input type='text' />}
 control={control}
 defaultValue={userData ? userData.phone : ''}
 name='phone'
 />
 </div>
 <button>Submit</button>
 </form>
 </div>
 );
}
export default UpdateUserData;

The called API is working well and the value is actually set to userData state.

{
 name: "John",
 phone: "02-98541566"
 ...
}

I also tried to setUserData with mock data in useEffect(), and it doesn't work either. Is there any problem in my above code?

asked Jun 7, 2020 at 8:16
0

19 Answers 19

216

@tam's answer is halfway to what is needed to make it work with version 6.8.3.

You need to provide the default value but also to useEffect to reset. That particular distinction is required if you have a form that you reload with another entity. I have a complete example in CodeSanbox here.

In a nutshell, you need to define your defaultValues in the useForm:

const { register, reset, handleSubmit } = useForm({
 defaultValues: useMemo(() => {
 return props.user;
 }, [props])
});

Then you need to listen to potential change.

useEffect(() => {
 reset(props.user);
}, [props.user]);

The example in the Code Sandbox allows swapping between two users and have the form change its values.

Grover
4,3982 gold badges32 silver badges80 bronze badges
answered Feb 18, 2021 at 21:20
Sign up to request clarification or add additional context in comments.

This is definitely one of the cleaner answers here.
@ Patrick, this works. Just a doubt when i tried without wrapping in the useMemo also it worked, the reason why we are using useMemo is that it won't recalculate on the next render until unless any of the dependency changed. Also any other diff using a useMemo and without using a useMemo directly passing the object since useEffect is already there? Can you correct me if this wrong - New to react :)
Just curious why are you using useMemo here?
This is correct.
The up-to-date answer is in this comment! stackoverflow.com/a/78149825/14802987
100

You can use setValue (https://react-hook-form.com/api/useform/setvalue).

Import it from useForm:

const { handleSubmit, control, setValue} = useForm({ mode: 'onBlur' });

Then call it with the user data after it's received:

useEffect(() => {
 if (userData) {
 setValue([
 { name: userData.name }, 
 { phone: userData.phone }
 ]);
 }
}, [userData]);

You can remove the default values from the form.

EDIT: See alternative answers below if this does not work.

Akash Kumar Verma
3,3582 gold badges20 silver badges33 bronze badges
answered Jun 7, 2020 at 9:02

looking for typescript version of setValue for all FormValues at once
@tommcandrew You are right, but we still need to add defaultValues to the form.
how can setValue be used for checkboxes and radio buttons?
did not work for me. got path split error. rather answer below worked.
I'm guessing the setValue definition has changed since this answer was written. It now takes a single name and value, as in setValue(fieldName, fieldValue). Setting a single field works with this method works; but is unfortunately not the goal.
60

setValue didn't work for me. Alternatively, you can use the reset method:

Reset either the entire form state or part of the form state.

Here is working code:

 /* registered address */
const [registeredAddresses, setRegisteredAddresses] = useState([]);
const { register, errors, handleSubmit, reset } = useForm <FormProps> ({
 validationSchema: LoginSchema,
});
/**
 * get addresses data
 */
const getRegisteredAddresses = async () => {
 try {
 const addresses = await AddressService.getAllAddress();
 setRegisteredAddresses(addresses);
 setDataFetching(false);
 } catch (error) {
 setDataFetching(false);
 }
};
useEffect(() => {
 getRegisteredAddresses();
}, []);
useEffect(() => {
 if (registeredAddresses) {
 reset({
 addressName: registeredAddresses[0].name,
 tel: registeredAddresses[0].contactNumber
 });
 }
}, [registeredAddresses]); 
Akash Kumar Verma
3,3582 gold badges20 silver badges33 bronze badges
answered Aug 10, 2020 at 15:47

Thanks for this, reset({ ...reduxState }) is what I had been looking for to retain consistency between formState and reduxState.
40

Found another easy way, I used reset API from useForm

 const { handleSubmit, register, reset } = useForm({ resolver });

After you call API and get back response data, you call reset with new apiData, make sure apiData key's are same as input keys (name attribute):

 useEffect(() => {
 reset(apiData);
 }, [apiData]);

form's default values are cached and hence once you get the data from API, we reset the form state with new data.

answered Oct 12, 2021 at 7:46

You dont need useEffect, just do this when you have recieved the apiData directly in the .then section.
That works too, It depends on Implementation as usually API calls and handling data are made in different files. @RollingInTheDeep
30

I know this post is a few years old but I stumbled upon it and found that there is now a better way than the accepted answer.

The documentation now has a prop called values that will reactively update (see https://react-hook-form.com/docs/useform#values).

Here is the example code:

function App() {
 const values = useFetch("/api")
 useForm({
 defaultValues: {
 firstName: "",
 lastName: "",
 },
 values, // will get updated once values returns, this can be a state variable like OP's userData state variable.
 })
}
answered Mar 12, 2024 at 20:19

As a side note, to fetch data from the server I value using something like react-query or TRPC which makes it cleaner not having to manage useEffect hooks or state variables yourself.
This is the up-to-date answer and should be highlighted more!
This is very underestimated and it must be accepted because we all must read manuals with much more attention.
22

@tommcandrew's setValue parameter formatting didn't work for me.

This format did:

useEffect(() => {
 const object = localStorage.getItem('object');
 setValue("name", object.name);
}, [])
answered Nov 28, 2020 at 6:18

This is working for me with React Native and a controller wrapper.
16

although this post is 2 months old, I stumbled upon this issue today and searched for a couple of ways to do it. The most effective way I've come up with is using useMemo to set your defaultValues, like this :

const { control, errors, handleSubmit } = useForm({
 reValidateMode: 'onChange',
 defaultValues: useMemo(() => yourDefaultValues, [yourDefaultValues]),
});

This allows you to properly set values in your form, without the struggle of multiple implementations if you happen to have field arrays (which was my case).

This also works while using the advanced smart form component exemple from the official documentation. Let me know if you have any questions !

answered Jan 27, 2021 at 15:50

I am running on 6.8.3 and the default value gets reevaluated each time the yourDefaultValues change but nothing happens on the form (I added debug statement in the useMemo). I have verified that the name are the same from the name of the component and the components are using inputRefand register. Do you have any idea?
10

This works for nested objects (I'm using version 6.15.1)

useEffect(() => {
 for (const [key, value] of Object.entries(data)) {
 setValue(key, value, {
 shouldValidate: true,
 shouldDirty: true
 })
 }
}, [data])
Akash Kumar Verma
3,3582 gold badges20 silver badges33 bronze badges
answered Apr 1, 2021 at 1:54

cleanest implementation imo
Although, eslint won't like the for of loop. A possible solution is Object.keys(data).forEach((val, i) => {})
this doesnt work, when you reset form you get empty fields and not with defaultValues. reset() instead of setValues mostly works but its flaky and loads undefined async values if you have few of them, i am still looking for proper solution
7

As of react-hook-form 7.41, you can use defaultValues with async functions like this:

const {
 formState: { isLoading },
} = useForm({
 defaultValues: fetch('API'),
 // resetOptions: {
 // keepDirtyValues: true
 // }
});

now the defaultValue field type look like this:

type AsyncDefaultValues<TFieldValues> = (payload?: unknown) => Promise<TFieldValues>;

isLoading for the async defaultValues loading state.

answered Feb 2, 2023 at 11:43

The most up to date answer
If you're fetching data inside the defaultValues key, this is either assuming that react-hook-form is going to be your state management tool or you're duplicating network requests. If you were using something like RTK Query, or Zustand, you'd want to make your calls in there and pass the data on to the forms, not having react-hook-form be the source of truth for your data.
i am using this method, how can i pass an default Image/avatar to control like webphoto?
4

Using reset is a simple solution.

const { reset } = useForm();
onClick={()=> reset({ firstname: 'Joe' }, { lastname: 'Doe' }) }
answered Jul 22, 2022 at 10:58

Comments

4

Documentation mentions a way to set the default values if the state changes here https://react-hook-form.com/api/useform/#values

function App({ values }) {
 useForm({
 values // will get updated when values props updates 
 })
}

I have had issues reseting the form using above method, as it is leading to an infinite loop.

react hook form Maximum update depth exceeded

So, instead i tried to reset these values using useEffect, but still it did not worked.

Now, i realised that if i have to reset entire form, then i have did something like below and it worked.

 useEffect(()=>{
 reset({...dataFromApi});
 }, [quote])

answered Apr 24, 2023 at 4:10

Comments

4

Many comments telling you to just "setValue" or "reset". But that's not appropriate.

My recommendation:

Wrap you form in a Component, fetch the data on parent Component and render your form only when your data is ready. Use "defaultValues"

example:

const UserForm = ({data}) => {
 const {...} = useForm({
 defaultValues:data
 })
}

const ParentForm = () => {
 const userData = fetch('...');
 return userData ? <UserForm data={userData} /> : <Loading />;
};
answered Jun 26, 2024 at 22:07

I like this approach as it becomes easy to test the component as well.
2

If you are using version 7, you may no longer need useEffect to do "loading defaultValue from async call (e.g. fetch)" on the form component mounts

example from v7.47 doc:

// set default value async
useForm({
 defaultValues: async () => fetch('/api-endpoint');
})

note:

  1. that async function defined in defaultValues should return the object required for defaultValues
  2. the call (defaultValues()) will be made on component mounts (same as useEffect with empty dependency array)
  3. if you need other dependency, use reset() to load your defaultValues (because they are cached)
answered Oct 18, 2023 at 11:00

This approach works, but I really don't see why you need to do all of this in the component itself. See my other reply here about using the HOC approach
1

There is a much better way to do this that I may have missed in the comments or in one of the answers.

Just send your defaultValues in as a prop to the form itself.

This higher order pattern works quite a bit better.

<UpdateAppointmentForm defaultValues={query.data} />

When I fetch from my system, I have the model I'm working with populated. Passing it directly down to the function ensures it re-renders with the data in the way I need.

In the <UpdateAppointmentForm> component, I can just use the useForm hook with the values instantiated:

interface Props {
 defaultValues: SchemaType;
}
export const UpdateAppointmentForm = (props: Props) => {
 const form = useForm<SchemaType>({
 resolver: zodResolver(Schema),
 reValidateMode: "onBlur",
 shouldFocusError: true,
 defaultValues,
 //...other stuff
 });
 // ... return your form
}

The approaches in here to manipulate the state on the fly just add unnecessary complication and other state juggling you really don't need to worry about. Let React's rendering cycle and component tree do the work for you.

Happy coding!

answered Mar 22, 2024 at 22:31

Comments

1

Easiest method for those interested,

// fetching data from clerk

const { user } = useUser();

// Creating default values based on information I am receiving from the parent component.

const defaultValues = initialData
 ? initialData
 : {
 name: "",
 stock: 0,
 description: '',
 negotiable: false,
 discount: 0,
 categoryId: "",
 creatorId: '',
 isFeatured: false,
 isArchived: false,
 sizes: [],
 colors: [],
 price: 0,
 images: [],
 };

//I removed defaultValues from the form as this only renders the values into the hook form once. The 'values' function on the other hand re-renders on input change and asynchronously adds your data to the form. Also note that since the data being fetched is undefined at first, I had to give it a fallback value of an empty string.

const form = useForm<ProductFormValues>({
 resolver: zodResolver(formSchema),
 values: { ...defaultValues, creatorId: user?.id || '' },
 });
answered Jun 4, 2024 at 9:22

Comments

0
 // single value change
 useEffect(() => {
 setValue("username", 'changed username', {
 shouldValidate: true,
 shouldDirty: true,
 shouldTouch: true
 })
 }, []);
answered Nov 7, 2023 at 9:39

Comments

0

we can set the default values of hook forms using reset provided by react-hook-forms

const { handleSubmit, control,reset} = useForm();
 const fetchUserData = useCallback(async account => {
 const userData = await fetch(`${URL}/user/${account}`)
 .then(res=> res);
 console.log(userData);
 if(userData.status===200||userData.status===201){
 reset(userData.data);
 }
 }, []);
useEffect(() => {
 const account = localStorage.getItem('account');
 fetchUserData(account);
 
 }, [fetchUserData])

this sets the values of the fields in form with the data fetched. this works only when the field names in both form and fetched matches. you can also specify the type of data for forms using interface if you are using typescript.

interface

interface props{
name:string,
id:number
}

useForm

const { handleSubmit, control,reset} = useForm<props>();

Comments

0

This solution worked for me

sessionData is the result of fetching data

useEffect(() => {
 sessionData &&
 reset({
 name: sessionData?.user?.name,
 username: sessionData?.user?.username,
 email: sessionData?.user?.emails.email,
 password: "",
 confirmPassword: "",
 });
}, [sessionData]);
answered Feb 10, 2024 at 22:16

Comments

0

I'm using React Hook Form along with Yup for validation. I know the common way to set default values is via useForm({ defaultValues }) or through useEffect. However, I found another cleaner way to define defaults directly in the Yup schema, like this:

 import * as Yup from "yup";
 
 const formSchema = Yup.object().shape({
 phone: Yup.number().default(0),
 });
 const { register, handleSubmit } = useForm({
 resolver: yupResolver(formSchema),
 });

Advantages:

  • Keeps validation and defaults in a single place
  • Easier to maintain and reuse schema definitions
  • No need to use setValue() manually
answered May 14, 2025 at 15:34

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.