Unform: the definitive form library for React apps
Tags: javascript, react, reactnative, opensource
Published: 2019-05-18 19:00:19

All of us, React developers - or even javascript developers that already heard about React ecosystem - know a thing: forms in React application are really painful and verbose (like [Formik's devs said too](https://github.com/jaredpalmer/formik)).

We have [Redux Forms](https://github.com/erikras/redux-form/), Formik and many other libraries to make forms happen in React. But the first not convince me... Why? I don't agree in keep form state inside Redux (could we continue friends?). And the second is very powerful... But (nothing matters if there is a "but" after it) by being very powerful, it becomes very verbose or complex for simple forms (and there is a problem with performance in big forms too, subject to another post). And most of these libraries work with controlled components for input fields. It does not always be the better thing to include in your project if you are going to work with big forms and/or need much performance.

What can we do so? Well, because of these points, a lifestyle/company/community called [Rocketseat](https://rocketseat.com.br/), located in Brazil, makes [Unform](https://github.com/Rocketseat/unform):

{% github rocketseat/unform %}

Let's see it in action!

> You can view the source code of this example in https://github.com/italomlp/unform-example

## 1. What we will need?
- [NodeJS and NPM](https://nodejs.org/) (LTS version?)
- [Yarn](https://yarnpkg.com/) (this is optional but faster)
- [Create React App](https://github.com/facebook/create-react-app)
- [Unform](https://github.com/Rocketseat/unform)
- [Yup](https://github.com/jquense/yup) (for validation)
- [React Datepicker](https://github.com/Hacker0x01/react-datepicker/) (a component for datepicker and demonstration of third party components integration)
- A browser (I use Google Chrome)
- A text editor (I use [VS Code](https://code.visualstudio.com/))
- Terminal (I use [Oh My Zsh](https://github.com/robbyrussell/oh-my-zsh))

## 2. Init a react app

    yarn create react-app form-example

or

    npx create-react-app form-example

or another way described in the create-react-app repository

You will see the following result:
![create-react-app result](https://i.imgur.com/H1kUi3B.png)

Then we can run our project with:

    cd form-example/
    yarn start

And see the following in browser:
![create-react-app start](https://i.imgur.com/IA62CgZ.png)

## 3. Clean src folder and Install dependencies
After creating the app, we will have this folder structure:
![react-app folder structure](https://i.imgur.com/nKSK5r5.png)

First, I'll remove `src/App.test.js`, `src/App.css`, `src/logo.svg` and `src/serviceWorker.js` files, that we will not use in this post.

    rm src/App.test.js src/App.css src/logo.svg src/serviceWorker.js

We need then to install our dependencies. The `yup` is a good choice for validation purposes (and is the recommendation from unform's team). And the `react-datepicker` is an awesome component to demonstrate the integration between Unform and third libs.

    yarn add @rocketseat/unform yup react-datepicker

So, we can start coding.

## 4. Make the form
If you look to your browser, see that the app does not compile anymore. To avoid this, we have to change our `src/index.js` to:

```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(<App />, document.getElementById('root'));
```

And our `src/App.js` to:
```jsx
import React from 'react';

function App() {
  return (
    <div>
      <h1>Contact form</h1>
    </div>
  );
}

export default App;
```

The use of Unform is very simple: we import things and use them in our JSX. Let's see.

```jsx
// ...
import { Input, Form } from "@rocketseat/unform";

function App() {
  return (
    <div>
      <h1>Contact form</h1>
      <Form>
        <Input name="fullname" label="Full name" />
        <Input name="phone" label="Phone" />
        <Input name="email" label="Email" />

        <button type="submit">Save</button>
      </Form>
    </div>
  );
}
```

This works well... 

#### But how can we get the data?
Simple: pass a handle submit function to Form component.

```jsx
// ...
const handleSubmit = data => {
    console.log(data);
};

return (
  <div>
    <h1>Contact form</h1>
    <Form onSubmit={handleSubmit}>
    {/* ... */}
```

Nice. The result is:
![unform working](https://i.imgur.com/pAQqtR4.png)

#### But, if I want nested fields?
Cool, just use the `Scope` component to wrap nested fields. As you can see, I added address fields like this:

```jsx
import { Input, Form, Scope } from "@rocketseat/unform"; // added Scope component import

// ...
<Form onSubmit={handleSubmit} >
  {/* other Input components */}

  <Scope path="address">
    <Input name="city" label="City" />
    <Input name="state" label="State" />
  </Scope>
  {/* ... */}
</Form>
```
The `Scope` component is used to tell to React: "hey, my children are properties of an object which I represent". And will resut in:
![unform scope](https://i.imgur.com/0eJHTDd.png)

#### And if I want to populate the form with initial values?
You can. Just pass an object to `initialData` prop of `Form` component:

```jsx
// ...
const initialData = {
  fullname: "Italo Menezes",
  phone: "999999999",
  email: "italo@email.com",
  address: {
    city: "Rio de Janeiro",
    state: "Rio de Janeiro"
  }
};

// ...

<Form onSubmit={handleSubmit} initialData={initialData}>
  {/* ... */}
</Form>
```

#### Ok ok. But validation sometimes is a pain. How can I make it with Unform?
For validation, we integrate with `Yup`, that is simply an object schema validator. Don't get me wrong when I say "simply". It is very powerful. What I mean is that power is not always the same that complex. So, let's see.

With object schema validations, we can declare rules to object properties, making them strings, with minimum or maximum length, matching a Regex, and so on.

We will validate our form by these rules:
- Full name has to have at least 5 chars. Required.
- The phone number has to be only numeric and have exactly 9 digits. Required.
- The email has to be in an email format (so obvious). Not required.
- The city is not required.
- The state is not required.

So, with Yup, let's create this object schema:
```js
const schema = Yup.object().shape({
  fullname: Yup.string()
    .min(5, "The FULL name is only this?")
    .required("Full name is required."),
  phone: Yup.string()
    .matches(/^[0-9]{9}$/g, "Is this a phone number?")
    .required("Phone is required."),
  email: Yup.string().email("Is this an email?"),
  address: Yup.object().shape({
    city: Yup.string().notRequired(),
    state: Yup.string().notRequired()
  })
});
```

And add it to `schema` prop in `Form` component:

```jsx
<Form onSubmit={handleSubmit} schema={schema}>
  {/* ... */}
</Form>
```

### Stop! Let's add a simple CSS. This project is becoming very ugly!
Replace the code of `src/index.css` with:

```css
body {
  padding: 10px;
}

input {
  display: block;
  margin-bottom: 10px;
}

label {
  display: block;
}

span {
  display: block;
  font-size: 10px;
  color: red;
  margin-bottom: 15px;
}
```

### Coming back to validations...
If you run this now and click on save without values on inputs, you will get this:
![yup validations](https://i.imgur.com/bwBeHGr.png)

#### Ok. All fine till now. And if I need to use my own input field in the form?
Well, the Rocketseat devs thought about this too, and make a hook to use with other components called `useField`.

We will use the `react-datepicker` that was added to our project in the beginning. To do this, we need to wrap it and add the `useField` like this:
```jsx
import React, { useEffect, useState, useRef } from "react"; // add the hooks

import { Input, Form, Scope, useField } from "@rocketseat/unform"; // useField hook
import * as Yup from "yup";

import ReactDatepicker from "react-datepicker"; // react datepicker component
import "react-datepicker/dist/react-datepicker.css"; // react datepicker css

// ...

const Datepicker = ({ name, label }) => {
  const ref = useRef(null); // for ref manipulation purposes
  const { fieldName, registerField, defaultValue, error } = useField(name); // the name of the prop in form object is used here
  const [selectedDate, setSelectedDate] = useState(defaultValue); // the state of our datepicker component

  useEffect(() => {
    registerField({ // here, we're registering the field in the whole form
      name: fieldName,
      ref: ref.current,
      path: "props.selected", // this is the path to selected date in ReactDatepicker (wich is the selected prop)
      clearValue: pickerRef => { // for reset purposes
        pickerRef.clear();
      }
    });
  }, [fieldName]);

  return (
    <>
      {/* the label is like label in Unform Input component */}
      {!!label && <label htmlFor="datepicker">{label}</label>}
      <ReactDatepicker
        id="datepicker"
        name={fieldName}
        selected={selectedDate}
        onChange={date => setSelectedDate(date)}
        ref={ref}
      />
      {/* the error is like error in Unform Input component */}
      {error && <span>{error}</span>}
    </>
  );
};

// ...
  <Form onSubmit={handleSubmit} schema={schema}>
    {/* ... */}
    <Datepicker name="birthDate" label="Birth date" />
    {/* ... */} 
  </Form>
```
Well, I added comments in the code, hope you understand. 

So, this will result in this:
![with datepicker](https://i.imgur.com/3zqNcES.png)
![result with datepicker](https://i.imgur.com/vaJx1VM.png)

> If you are not familiar with React hooks, I recommend one reading:
Hooks at a glance: https://reactjs.org/docs/hooks-overview.html

#### And finally, if I want to reset values after submit?
The `onSubmit` function of `Form` has a second parameter which is an object. This object has (till the date of this post was wrote) only one property helper, called `resetForm`. We can use it like this:

```js
const handleSubmit = (data, { resetForm }) => {
  console.log(data);
  resetForm();
};
```

------

We finished here. You can see more examples and docs in the Unform repository on Github. Unform is in its initial releases, but has an awesome community involved and the best devs I've never meet.

If you like this post, share and give it a :heart:. Also, you can follow me on Github and social media. Thanks for reading, and see you later!