React Hook Form
Setup
Run this command
npm install react-hook-form@7
Add the script
index.html
...
<script src="/node_modules/react/umd/react.development.js"></script>
<script src="/node_modules/react-dom/umd/react-dom.development.js"></script>
+ <script src="/node_modules/react-hook-form/dist/index.umd.js"></script>
<script src="/node_modules/@babel/standalone/babel.min.js"></script>
...Add this code
main.js
console.log(window.ReactHookForm);
Open the Chrome DevTools to the console tab similar output to what is shown below
{Controller: ƒ, Form: ƒ, FormProvider: ƒ, appendErrors: ƒ, get: ƒ, …}
Getting Data from the Form
Manually Using Controlled Components
- This is the same example from the prior Forms section provided here for easy comparison.
function SigninForm() {
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');
const handleSubmit = (event) => {
event.preventDefault();
console.log(username, password);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
value={username}
onChange={(event) => setUsername(event.target.value)}
/>
<input
type="password"
name="password"
value={password}
onChange={(event) => setPassword(event.target.value)}
/>
<button type="submit">Sign In</button>
</form>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<SigninForm />);
Using React Hook Form
Run this code
const { useForm } = window.ReactHookForm;
function App() {
const { register, handleSubmit } = useForm();
const signIn = (data) => {
console.log(data);
};
console.log({ ...register('username') });
return (
<form onSubmit={handleSubmit(signIn)}>
<input type="text" {...register('username')} placeholder="Username" />
<input
type="password"
{...register('password')}
placeholder="Password"
/>
<button type="submit">Sign In</button>
</form>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);Add this log statement to see what the React Hook Form library is doing for you.
const { useForm } = window.ReactHookForm;
function App() {
const { register, handleSubmit } = useForm();
const signIn = (data) => {
console.log(data);
};
+ console.log({ ...register("username") });
return (
<form onSubmit={handleSubmit(signIn)}>
<input
type="text"
{...register("username")}
placeholder="Username"
/>
<input
type="password"
{...register("password")}
placeholder="Password"
/>
<button type="submit">Sign In</button>
</form>
);
}
ReactDOM.createRoot(document.getElementById("root")).render(<App />);Open the
Chrome Devtools
and view theConsole
output
{name: 'username', onChange: ƒ, onBlur: ƒ, ref: ƒ}
Do you see how it's doing what we did manually previously?
Contact Us Form
const { useForm } = window.ReactHookForm;
function ContactUsForm() {
const { register, handleSubmit, watch } = useForm();
function send(data) {
console.log(data);
}
return (
<form onSubmit={handleSubmit(send)}>
<select {...register('department')}>
<option value="">Select...</option>
<option value="hr">Human Resources</option>
<option value="pr">Public Relations</option>
<option value="support">Support</option>
</select>
<br />
<p className="alert"></p>
<br />
<textarea {...register('message')} cols="30" rows="10" />
<br />
<input {...register('agreeToTerms')} type="checkbox" />
I agree to the terms and conditions.
<br />
<button>Send</button>
<pre>{JSON.stringify(watch())}</pre>
</form>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<ContactUsForm />);
Tip: The
watch
function causes the component to render again. UsegetValues
if you don't want to trigger the component to render again (which is most use cases).
Validation
Contact Us Form with Validation
const { useForm } = window.ReactHookForm;
function ContactUsForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
function send(data) {
console.log(data);
}
return (
<form onSubmit={handleSubmit(send)}>
<select
{...register('department', { required: 'Department is required' })}
>
<option value="">Select...</option>
<option value="hr">Human Resources</option>
<option value="pr">Public Relations</option>
<option value="support">Support</option>
</select>
<br />
{errors.department && (
<p className="alert"> {errors.department?.message}</p>
)}
<br />
<textarea
{...register('message', { required: 'A message is required' })}
cols="30"
rows="10"
/>
<br />
{errors.message && <p className="alert"> {errors.message?.message}</p>}
<input
{...register('agreeToTerms', {
required: 'You must accept the terms to send',
})}
type="checkbox"
/>
I agree to the terms and conditions.
<br />
{errors.agreeToTerms && (
<p className="alert"> {errors.agreeToTerms?.message}</p>
)}
<br />
<button>Send</button>
</form>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<ContactUsForm />);
Loading Default Values
This section explores how to load initial values from an object including how to load them asynchronously when communicating with an API to retrieve the data.
Default Values
const { useForm } = window.ReactHookForm;
function ContactUsForm() {
const { register, handleSubmit } = useForm({
defaultValues: {
department: 'support',
message: 'I need a refund.',
agreeToTerms: true,
},
});
function send(data) {
console.log(data);
}
return (
<form onSubmit={handleSubmit(send)}>
<select {...register('department')}>
<option value="">Select...</option>
<option value="hr">Human Resources</option>
<option value="pr">Public Relations</option>
<option value="support">Support</option>
</select>
<br />
<p className="alert"></p>
<br />
<textarea {...register('message')} cols="30" rows="10" />
<br />
<input {...register('agreeToTerms')} type="checkbox" />
I agree to the terms and conditions.
<br />
<button>Send</button>
</form>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<ContactUsForm />);
Default Values from API (Async)
const { useForm } = window.ReactHookForm;
let contact = {
department: 'support',
message: 'I need a refund.',
agreeToTerms: true,
};
let contactAPI = {
find(id) {
//calling api here in the real world
return Promise.resolve(contact);
},
};
function ContactUsForm({ id }) {
const { register, handleSubmit } = useForm({
defaultValues: async () => {
let contact = await contactAPI.find(id);
return contact;
},
});
function send(data) {
console.log(data);
}
return (
<form onSubmit={handleSubmit(send)}>
<select {...register('department')}>
<option value="">Select...</option>
<option value="hr">Human Resources</option>
<option value="pr">Public Relations</option>
<option value="support">Support</option>
</select>
<br />
<p className="alert"></p>
<br />
<textarea {...register('message')} cols="30" rows="10" />
<br />
<input {...register('agreeToTerms')} type="checkbox" />
I agree to the terms and conditions.
<br />
<button>Send</button>
</form>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<ContactUsForm />);