If you are developing a web application, sooner or later you will probably want some forms in it and therefore a need for some form management solution will arise.
If the application is built on top of React and Ant Design, which is a UI component library, it may be tempting to use its form solution, which is now quite feature packed, fully integrated into the framework and is proclaimed to be highly performant.
Let’s look at how it works in the real world.
The library automatically manages form state and validation just by using its components in a recommended way, no wiring is required. But it also provides ways to customize the behaviour and control the forms programmatically through API.
It’s also well integrated with the input components, so as a solution for most use cases to work out of the box it’s a good choice.
Most form solutions provide either a way to get the values object that will automatically rerender the component if it changes or a convenient way to watch (like React Hook Form) all or some values and react to change. This is especially handy in case of conditional fields (or sections) and also for various listeners reacting to values.
Ant forms provide a way to do that in a render function of a Form.Item component, which is good enough for simple use cases, but has some limitations.
First of all you can only work with values inside the render function, which means you cannot call hooks. To do that, you have to create a wrapping component, which can make simple calls quite convoluted. Next problem is you can’t specify which fields you want to listen to, which may cause unnecessary rerenders and cause performance problems.
There is a way how to get values outside of the form, but it has a limitation that programmatic changes of values circumvents this mechanism:
Ant Form works in such a way that it registers fields on mount and only then they are considered part of the form.
The consequence is that if you have a conditionally rendered field that isn’t rendered in the beginning or a field that’s in some popup, it will not be registered and if you attempt to set it, it will be ignored.
The workaround is to always render all possible fields with
noStyle prop set to true, which is easy to forget until a bug appears.
This is a design decision, as can be seen in a discussion in Github issue: https://github.com/ant-design/ant-design/issues/8880
There is a common pattern to convert between the input representation and store representation in most form libraries, usually by a format/parse configuration pair. This is especially useful for non-string values that are displayed as a simple HTML form element only supporting string values. For example a boolean value may be displayed as a pair of radio buttons with values “true” and “false” and represented as a boolean in store thanks to these decorators.
While Ant’s API provides
normalize prop for Form.Item, there is no corresponding opposite function, which makes its usage very limited.
The workaround is to make a wrapping component and apply decorators there, which is doable but inconvenient. It would be nice if Form.Item could take a render function as a child, which could take value and onChange as arguments, but right now it only takes React node and automatically injects those two props, which is nice magic for simple uses, but makes customizability quite cumbersome.
Other than the already mentioned issues there are also some that are of little importance but that have caused me a little headache at times.
There is no possibility to perform silent validation – meaning just getting the validation result without showing the validation messages. This could be useful for enabling/disabling some button that requires a valid form. In that case you don’t want to show the validation messages before the user has interacted with them at all, but you need to know the result.
One workaround I have used was to render the duplicate form in a hidden container and run validation there, which is not the nicest and most performant solution. Fortunately the requirement with button was abandoned, so this could be dropped and a pursuit of a better workaround was stopped.
There also seem to be requests for such feature in the community: https://github.com/ant-design/ant-design/issues/13296
Some other workarounds are provided, but no such first class feature will probably be provided.
The next thing causing problems is a deletion of items in Form.List. When you delete an item using Ant’s remove function, it firstly sets value to undefined (so an array seems like this: [“VALUE1”, “VALUE2”, undefined] for one render tick and only then it is removed from the array. This is mainly a problem if values are complex objects. In that case you have to be aware of this and preprocess the value array in some way to prevent errors of accessing fields in undefined value.
This seems to be a design decision as documented in Github issue: https://github.com/ant-design/ant-design/issues/28929
I think that Ant’s form solution is quite feature rich, allows for a high level of customization and is still being developed. After spending some time learning how it works and what quirks it has, it’s a perfectly usable solution for most needs and since it’s already available with Ant, it may be a good quick choice. In the near future Ant team hopefully solves the minor issues and decides to use design choices similar to the more prominent form librarier.
Personally I sometimes feel like I have to fight it and I wouldn’t choose it for my next project. I would consider one of the promising libraries like Formik or React Hook Form.