Forms are the most common interacting tool that we can encounter in many applications and also the challenging part to collect and preserve the data. There are multiple libraries that we can use such as React Final Form, Formik, Redux Form and React Hook Form. In this article we will focus on how React can solve the problem of working with forms and we will discuss two well-known libraries called React Hook Form and Formik.
Into React Components
Firstly, I would like to explain the difference between the controlled and uncontrolled components. The HTML elements and differ from the other DOM elements by having their proper internal state. React’s official page recommends giving the control of changing the value of these form elements to the library itself, since in React there is the principle of ‘the state is the single source of truth’. In this way, the components which have their states managed by React are called “controlled components”. On the other hand in the official page, also mentions that we may use uncontrolled components: like writing an event handler and pipe all of the input state through a React component.
Uncontrolled components are like traditional HTML form inputs, “ref” is used to get the form values and is simpler to implement form inputs. In controlled components, every time the user changes input value, the form is also updated and it saves the data in its state. This feature can be useful when we want to do in-place validation and force a specific input format.
Formik vs React Hook Form
According to React documentation, Formik is a good fit if we want a complete solution including validation, keep track of visited fields and handling form submission. Formik uses controlled components. Also since we can keep form state localized and managed by Formik it is good for building multi-step forms.
On the other hand, React Hook Form uses uncontrolled components. Since uncontrolled components keep the source of truth in the DOM, it is easier to integrate React and non-React code. React Hook Form isolates input components from the others and prevents re-render of the form for a single input. It avoids this unnecessary re-rendering. So it is a great advantage in terms of the performance compared to Formik which updates every change in every input field.
In Formik you can build forms in two different ways, one with using a Formik component and Formik-provided fields and the second way is by using the useFormik hook. With the first method it enables all features and this could harm the potential of Formik. For simple forms it is recommended to use useFormik hook. With useFormik we cannot work with components like , , , and connect() because they require React Context.
In our example we will create a multistep form with the Formik component and React Hook Form. In the first example with Formik library, we wrap our steps inside of Formik component:
<Formik
initialValues={
{firstName: '',
lastName:'',
email:'',
age: '',
birthday: '',
companyName: '',
companyYear: ''}
}
validationSchema={currentValidationSchema}
enableReinitialize
onSubmit={handleSubmitFormik}
>
{({ isSubmitting, handleChange, values }) => (
<Form className="App">
<div className="App”>
{renderStepFormik(activeStep, handleChange, values)}
{activeStep===3 ?
"":
<button
disabled={isSubmitting}
className="next"
type="submit"
color="primary"
>
»
</button>
}
</div>
</Form>
)}
</Formik>
In the first step, we have first name, last name and email; in the second: age and birthday; and in the last one company name and year. Inside of the renderStepFormik function, we have a switch to render every step with its related fields and Form component:
function Form({formData, handleChange, values, register} : FormProps){
useEffect(() =>console.log("Render times:", renderTimes++));
return (
<div className="App">
<Grid container spacing={3}>
{formData.map(({id, label, type} : FormData) => {
return (
<>
<Grid item xs={12}>
<TextField
id={id}
label={label}
name={id}
type={type}
onChange={handleChange}
value={getIn(values, id)}
/>
</Grid>
</>
);
})
}
</Grid>
</div>
);
}
Inside of the useEffect hook, we calculate the times that re-renders the Form component. As we can see in the gif below the Form component re-renders 42 times. Let’s compare the same multi-step form with the React Hook Form library.
With React Hook Form, we need to create three components for the three steps. Since React hook form keeps field values in DOM, if we use the same Form component, we see the first step filled values in the second step as prefilled. We use the register function from the library to set the field values to submit.
function Form({formData, register, handleSubmit, step, setStep} : FormProps){
const onSubmit = (data : Object) => {
if (step===3) {
alert(JSON.stringify(data, null, 2));
}
setStep(step)
}
return (
<div className="App">
<form onSubmit={handleSubmit(onSubmit)}>
<Grid container spacing={3}>
{formData.map(({id, label, type} : FormData) => {
return (
<>
<Grid item xs={12}>
<TextField
id={id}
fullWidth
label={label}
name={id}
type={type}
{...register(id)}
/>
</Grid>
</>
);
})
}
</Grid>
<button
className="next"
type="submit"
color="primary"
> »</button>
</form>
</div>
);
}
As we can see below, the component renders 3 times. Since our source of the truth is DOM with React Hook Form, it doesn’t re-render everytime the input changes. This could be great for the performance optimization when we have many fields in a simple form.
Conclusion
To sum up, in our Multi-step form example, while React Hook Form prevented the whole form to re-render for a single field change, Formik updated it when there were changes in the input. It is certain that there is less code written with Formik since we render only one component and with React hook form three, but also we should consider that we would have cost on performance because of the re-renders. According the requirements of our application we should decide which one should be the appropriate. Formik can be useful when we want to do in-place validation and give specific input format. Besides that since we keep form state localized it is advantageous for building multi-step forms. React hook form is great for simple forms and it does not have any dependencies. At the end, both libraries are very useful to implement forms with React.
Links of interest
Looking for the Best React Form Library? It’s Probably on This List by Peter Mbanugo @Dev.To
React Hook Form vs. Formik: A technical and performance comparison by Siegfried Grimbeek @LogRocket
Why You Should Choose React Hook Form Over Formik and Redux-Form by Madushika Perera @Bits and Pieces
React Hook Form VS Formik by Nathan Sebhastian @Bits and Pieces