Skip to main content

React Hook Form

Setup

  1. Run this command

    npm install react-hook-form@7
  2. 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>
    ...
  3. Add this code

    main.js

    console.log(window.ReactHookForm);
  4. 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

  1. 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 />);
  2. 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 />);

  3. Open the Chrome Devtools and view the Console 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. Use getValues 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 />);

Reference