Written by Diogo Souza✏️
The more modern the application, the more likely developers will need to use special features and helpful hints to ensure demanding clients are happy with their user experience.
In the world of React, forms give us all the power of input components — but this power isn’t enough.
We need better and faster ways to create customized components involving inputs, selects, buttons, and potentially new components that are not recognizable by our browsers implicitly (i.e., create new UI experiences in terms of component).
We need to validate data in many different forms, sometimes via complex regex or JavaScript validation functions, and sometimes through external resources.
In certain cases, we may need to store data locally in the navigator and recover it wisely. We’ll also need to communicate those components in our own way.
React doesn’t give developers a way to deal with custom highlights, so the community came up with ways to do it themselves.
We have dozens of different options. We have libs for basic form manipulation, using with Redux, etc.
At this point, the best option for users seems to be Formik—at least that’s what the numbers show us.
The image below displays the most downloaded npm packages for famous React form libraries (as per the writing of this article) at npmtrends.com:
Formik is by far the favorite. Not only is it flexible and communicative with React, but it also allows developers to easily integrate with Yup (a popular JavaScript object schema validator and object parser).
Perhaps its most important feature is form state management — we no longer need to keep calling the Redux store’s state on every keystroke (which is indeed bad practice) once the state is locally and automatically maintained by Formik.
While Formik is good with controlled components, it’s not as adept at handling uncontrolled ones.
Unform, on the other hand, is focused on high performance for React forms and nested structures (particularly deep ones). Unform also allows you to create strong relationships between your components—even your uncontrolled components—without sacrificing anything performance-wise.
This Brazilian React library also works very well with React Hooks.
In this article, we’re going to look at a few examples that demonstrate some of the potential of this library.
Form Creation
First, let’s take a look at how both libraries handle form creation. Below, we can see a basic Formik form usage:
import React from 'react';
import { Formik, FormikProps, Form, Field } from 'formik';
export class SampleForm extends React.Component {
handleSubmit = (values, {
props = this.props,
setSubmitting
}) => {
console.log(values);
// submit logic here
setSubmitting(false);
return;
}
render() {
return(
<Formik
initialValues={{
email: '',
document: ''
}}
validate={(values) => {
let errors = {};
// error validations here
return errors;
}}
onSubmit={handleSubmit}
render={formProps: FormikProps => {
return <Form>
<Field ... />
// other fields...
<button type="submit" disabled={formProps.isSubmitting}>
Submit
</button>
</Form>;
}}
/>);
}
}
Refer to the official React docs for React specifications. Most of these libraries usually advise that developers start with the JavaScript submit function.
In our case, this function has two parameters: values
, which represent the form fields’ values, and a second object with properties and functions from Formik for free use in the submit body function.
The setSubmitting
(a boolean), for example, is a useful mechanism for analyzing whether the request is currently happening or not.
Each Formik form is made of a main element <Formik>
, and some important props:
-
initialValues
: the local state value for each subsequent controlled component -
validate
: receives all the form’s values as parameter. You can use this function to perform whatever validations you want. You can also use it to set and return the proper error codes/messages. -
onSubmit
: determine which function will handle the submit event. -
render
: the form render function itself. Decide which are the controlled Formik components and which are the uncontrolled HTML components of your form.
Pretty simple, isn’t it? Let’s take a look at the equivalent Unform form below. Make sure to have react
, react-dom
and yup
packages already installed.
import React from 'react';
import { Form, Input } from '@rocketseat/unform';
import * as Yup from 'yup';
const schema = Yup.object().shape({
email: Yup.string()
.email('Custom invalid email message')
.required('Custom required message'),
document: Yup.string()
.max(11)
.required(),
});
function SampleForm() {
const initialValues = {
email: '',
document: ''
};
function handleSubmit(values) {
console.log(values);
// submit logic here
}
return (
<Form onSubmit={handleSubmit} initialData={initialValues} schema={schema}>
<Input name="email" />
<Input name="document" type="number" />
<button type="submit">submit</button>
</Form>
);
}
First off, we need to install the Unform via:
yarn add @rocketseat/unform
-- or via npm
npm install -g unform
The first thing we need to do is import the respective Form
and Input
components from Unform.
The second thing you’ll see is related to a Yup validation schema.
Like Formik, Unform easily integrates with Yup schemas by providing a schema
property at its <Form>
component. Since Yup is, by far, the most popular lib for input values validation, it’s pretty straightforward to use.
This code provides a single example to help you better understand Unform with validations like email, required fields, and maximum value length.
When Unform works with Hooks, the class-based component style is abandoned in favor of a single-function component.
The initialValues
from Formik translates to initialData
here — make sure to match each object property to each input name to ensure values are applied correctly.
The handleSubmit
function loses the parameters from Formik and simply receives the values for manipulation in the submit event.
Lastly, there’s no internal render
function, which means that your <Form>
must be mixed with your other components.
You can also use other common properties like placeholder
, style
, etc.
Other elements
Select
Let’s analyze a second example with comboboxes, which are pretty common elements we need in forms.
Select dropdowns usually look like this:
<Form>
<Field name="country" component="select" placeholder="Select a country">
<option value="andorra">Andorra</option>
<option value="argentina">Argentina</option>
<option value="aruba">Aruba</option>
</Field>
<button type="submit">Submit</button>
</Form>
Not complex. Unform would simplify it a bit by allowing you to provide an array of objects, just like that:
const options = [
{ id: 'andorra', title: 'Andorra' },
{ id: 'argentina', title: 'Argentina' },
{ id: 'aruba', title: 'Aruba' },
];
<Form>
<Select name="country" options={options} placeholder="Select a country" />
<button type="submit">Submit</button>
</Form>
Remember to import the Select
element from the Unform library in the beginning of the file.
Nested elements
When it comes to multiple and nested elements, no library provides a fully adaptable and working solution.
Formik has a very handy object called <FieldArray>
, which helps with common array/list manipulations:
let countries = ['andorra', 'argentina', 'aruba'];
<Form>
<FieldArray
name="countries"
render={arrayHelpers => (
// defining your form, loop through `countries`
)}
/>
</Form>
It also has a bunch of familiar functions like pop
, replace
, push
, insert
, and others for the automatic injected arrayHelpers
that help a lot with item manipulation.
However, whenever you want to nest items and apply validations or organize the forms in a way that’s closer to your entity model, Formik lacks options.
Unform has an interesting mechanism for dealing with nested objects. Take the following code as an example:
import React from 'react';
import { Form, Input, Scope } from '@rocketseat/unform';
function App() {
function handleSubmit(values) {
console.log(values);
}
return (
<Form onSubmit={handleSubmit}>
<Input name="name" />
<Scope path="address">
<Input name="country" />
<Input name="zipCode" />
</Scope>
<button type="submit">Submit</button>
</Form>
);
}
Scope
is an Unform component that marks the root of your nested element. It’s just for markup purposes and doesn’t hold any value.
When you submit the form your values
object would look like this:
{
name: '',
address: { country: "", zipCode: "" }
}
Whenever you update the state’s value, it will reflect on your form fields.
React Hooks
React Hooks are a recent addition to React that helps us create components by directly manipulating the state without converting them into class components.
Furthermore, Hooks allow us to create our own snippets of code that can become a Hook themselves. You can use these Hooks anywhere the functionality is needed.
For example, say you wanted to create a form with some random or very specific code mounted on some props that come from the parent component and — at the same time — make use of the useState React Hooks feature:
import React, { useState } from ‘react’
function Form(props) {
const [count, setCount] = useState(0)
let { handleSubmit } = props
return (
<form onSubmit={handleSubmit}>
// some input generated/random code
<button
type=“button"
onClick={() => {
setCount(count + 1)
}}
>Increment</button>
<button type=“submit">Submit</submit>
</form>
)
}
export default Form
Using the component is pretty straightforward from what we’ve seen so far:
<Formik render={props => <Form {…props} />}
It’s important to do the proper imports at the beginning of the file, as well as in the Form element we’ve just created. Using Unform is basically identical to this process, with certain changes for its rendering system.
Conclusion
When choosing between Formik and Unform, it’s all about finding the best fit for your project purpose.
Unform is a great library, especially because it’s lightweight, performative, and flexible enough to allow integration with other libraries. You may want to use a third-party component in your forms, like react-select and react-datepicker. With Unform, that’s easy to do.
Go ahead and try it yourself. Migrate some components, or create components from scratch. Make use of React Hooks for more concise code, and test a different fields organization for nested elements.
And don’t forget to check the official docs for more on the other elements, as well as examples of each one.
Editor's note: Seeing something wrong with this post? You can find the correct version here.
Plug: LogRocket, a DVR for web apps
LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
Try it for free.
The post Comparing React form builders: Formik v. Unform appeared first on LogRocket Blog.
Top comments (0)