I am building a front-end application using React, TypeScript, and React Hook Form for form management.
I decided to run some tests to try to better understand how React Hook Form works, and I ended up encountering a "problem" when using formState.errors
.
Basically, after the form is submitted, the component is rendered more than once, regardless of whether formState.errors
contains errors or not.
I looked in the documentation, and I couldn't find anything about this, so I don't know if it would be expected behavior, a bug, or a failure in my implementation...
The thing is, it seems incorrect to me that more than one re-render occurs when the form has been submitted only once.
Does anyone know what might be happening?
import * as React from "react";
import * as ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
type FormInputs = {
firstName: string;
};
const App = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormInputs>({
defaultValues: {
firstName: "",
},
});
function onSubmit() {
console.log("onSubmit");
}
console.log("errors", errors);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label>First Name</label>
<br />
<input type="text" {...register("firstName", { required: true })} />
<br />
<input type="submit" />
</form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I expected the componentto be rendered only once after the form was submitted.
According to the author of React Hook Form, executing handleSubmit
should make the component render twice.
On CodeSandbox, logging the errors
object might show four renders, but running the same code locally, it results in only two renders. (This might be due to how CodeSandbox handles updates, you could try change it to console.log("hello world")
and see it only prints twice instead of four times)
To understand what's happening, we can modify the code to print the validation stages, using useEffect
:
const App = () => {
const {
register,
handleSubmit,
formState: { errors, isSubmitting, isSubmitSuccessful },
} = useForm<FormInputs>({
defaultValues: {
firstName: '',
},
});
const onSubmit = (data: FormInputs) => {
console.log('onSubmit:', data);
};
useEffect(() => {
console.log('errors updated:', errors);
}, [errors]);
useEffect(() => {
console.log('isSubmitting:', isSubmitting);
}, [isSubmitting]);
useEffect(() => {
console.log('isSubmitSuccessful:', isSubmitSuccessful);
}, [isSubmitSuccessful]);
console.log('App rendered');
return (
...
);
};
Once we type "test" as input and click submit, we can see the validation stages taking action in the console:
App rendered
isSubmitting: true
onSubmit: {firstName: 'test'}
App rendered
errors updated: {}
App rendered
isSubmitting: false
isSubmitSuccessful: true
So basically:
Here you can actually see it even renders three times, because of isSubmitting
value changing as well.