Optimise your large Formik forms in Create React App

Matt Faircliff
4 min readJul 26, 2019

--

Large web forms: lots and lots of little boxes

TL;DR

A year and a half after I wrote this article…

The passage of time has wizened me to the ways of the grand masters and I must say that pretty much all of the below is less applicable now that I know Immutable.js. That said, much of the below still applies, however going forward I would almost always reach for Immutable for my container components and redux store logic. Additionally, I would also use React Redux’s connect method’s mergeProps attribute to ensure that a presentational component receives only the props it actually needs, and no more.

If you use big forms in React you may have experienced performance issues sometime during the development process. There are various methods to optimise your forms to improve speed when dealing with a large amount of form inputs. The below is one such method that deals with the problem by grouping fields and intelligently only updating the individual group of fields if it’s values, or any values the inputs in the group depend on change.

Example code for the following demo can be found here.

Big React forms can have performance problems

We already know that Create React App, Formik and the yup validation library work really well together. Combine them with Redux and React Router and you are well on your way to your next awesome front-end app.

However, time passes, scope creeps and you realise those Formik forms are getting quite big. 50 or 60 fields in one form and your app starts to feel sluggish and unresponsive, especially when filling in forms with your browser’s devtools open. Scale that up to a 100+ fields in a form and things come grinding to a halt.

One the requirements of our forms (and most likely all large forms out there) is field dependency (or dynamic inputs): the values of certain fields determine the options, visibility and validation requirements of other fields. Each field is therefore it’s own component which receives the Formik render props. Based on configuration logic (also passed in as a prop to the respective input component) and the values of other fields (determined from the Formik render prop), the options, validation requirements and visibility of the child input components change.

Formik offers an option to speed things up in terms of validation on each keystroke, so you turn off validateOnChangeand hope for the best. It helps a little but things are still far from perfect as each keystroke causes a re-render of all child components within the form (due to the valueschanges in the Formik render prop).

Inspecting things a little deeper using your devtools profiler (flame graph) or https://github.com/welldone-software/why-did-you-render, it’s apparent that the all Formik child input components (ie. your text inputs, selects, textareas, checkboxes etc.) are re-rendering on each keystroke. Why? Because being dynamic inputs they have to: their props are changing. This is a fairly complex problem to solve, or is it? Today I will share our team’s solution to this issue: using field groups and watchers, which is likely just one way to get around this problem. If you have any ideas to improve, please comment and share.

1. The config

Before defining the Formik form and child input components, we begin with the declarative JSON config:

The above is abbreviated (there could be many, many more fields) but I think you get the idea.

2. The Formik form

The central piece of this puzzle is the Formik form. We set it up in a pretty standard fashion:

3. The field group

The FieldGroup component is where the magic happens. It groups like fields and then uses logic configured in the JSON config file to decide if the group should update or not when changes occur to any of the form inputs. In affect this creates mini-forms within the form, thereby optimising the speed of the updates overall. The fields are rendered using the Formik Field component which automagically handles onChange and value updates of each input.

In short, each time the Formikprops.valueschanges all field groups in the form will render, however inside each field group exists the logic to decide if this group is directly involved in the change (ie. inputs in the group itself have changed value) or if any of the changes made affect the group (ie. non-group fields which conditionally affect inputs in this group). If either of these conditions evaluate to true the group will re-render, otherwise the render will be avoided in the shouldComponentUpdatelifecycle method.

Field groups only render when necessary

As you can see above, even though all the group’s props are changing on each keystroke, only the groups which are directly affected by the change are updated. This creates massive performance benefits on large forms.

Example code for this demo can be found at the below link. Feel free to fork or submit PR’s to improve it. Also, please let us know your comments below (or even clap) and let’s make this world, and web forms, better together.

Final note…

The above ‘pattern’ for optimising form updates was conceived by the brilliant Julian Rachmann before I got my hands on it and completely ruined things. All kudos and blame should be directed at him 😉.

If you enjoyed this article please encourage me to write more by liking it. Also, check out another one of my articles about the container / component pattern here.

Photo by sergio souza on Unsplash

--

--

Matt Faircliff

javascript, react, python, django and everything in between