Yomi Eluwande JavaScript Developer. Wannabe Designer and Chief Procrastinator at Selar.co and Worklogs.co

An imperative guide to forms in React

9 min read 2694

So, you just got started with React and you’ve started building React apps and you’re wondering, how do I work with forms in React? Fret not.

In this tutorial, I will explain how React deals with forms and events. I’ll also walk you through how to write code that helps to get input from the different form components and submit the data in a React application.

Finally, I’ll show how to use the Formik library to build all kinds of forms, from simple to complex.

Forms and events in React

Before we begin to write code, it’s important to note how React deals with forms and events. Consider this simple input tag below:

<input type="text" placeholder="First Name">

In order to get the data inside the input tag, you need to be able to fetch the content somehow. This doesn’t come easy for React, as there’s no preset API to help with this like Vue.js’ v-model or Angular’s ng-model.

It doesn’t come easy, but it can be done. How?

By making sure that the view (the input, select, or textarea field) is always in sync with the state. This means that the value for the input element must first be created in the state and then set to that state value in the render() function.

Something like the code block below:

As you can see in the code block above, the input element has a value of this.state.inputvalue which means the value of the input element is set to be in sync with the inputvalue in the state. This essentially translates to the fact that whatever is typed in the input field will be stored in the state, but there’s a problem here.

React doesn’t know how to detect if there’s a change in the input element if there was an addition or deletion. Use of a form event handler — onChange to check for changes.

This will lead to the code block below:

In the code block above, the input element now has an addition to the onChange event. The onChange event will be executed whenever there’s a change in input element and it’s set to execute the handleChange() function.

The handleChange() function will always automatically set the state to the current value of the input. One more thing to note is the addition of a new line of code inside the constructor.

this.handleChange = this.handleChange.bind(this);

The line of code above is used to bind the handleChange() function. This is done because in JavaScript, class methods are not bound by default. If this is not done, the handleChange() function will return undefined when the onChange event is called.

So React knows how to store the values from the input element to the state now, but how do we deal with form submission. Very simple. Take a look at the code block below:

This is essentially the same code block as seen above, but with a few additions. The form now has an onSubmit event that executes the handleSubmit function.

The handleSubmit() function does two things; it logs the current value of the input element whenever the form is submitted and most importantly it prevents the default HTML form behavior of browsing to a new page. The line of code below is also added to the constructor so as to bind the handleSubmit function.

this.handleSubmit = this.handleSubmit.bind(this);

It’s important to note that there are different methods to binding the functions. Binding them in the constructor is one way, but we can also use fat arrows.

See the code block below for reference:

The syntax above makes sure that this is bound within the handleSubmit function.

The whole process above of syncing the value of the input element to the state is called Controlled Components. We essentially made the React state the single source of truth. The React component that’s responsible for rendering the form is also responsible for what happens whenever a user adds anything to the form.

Now that we’ve seen how React deals with forms and how to ensure that the values are in sync with the state, let’s build a proper form with different types of fields, that is, <input>, <textarea>, <select>, radio e.t.c

The code block/demo below has all the code needed to demonstrate how to deal with the different form fields. We’ll be going through each of them to see how it works.

Input

The code implementation for the input field is straightforward. The value is set to be in sync with fullname which is already declared in the state. The onChange event is used to execute the handleChange function if there’s a change in the input field.

The only new thing on the input field above is the addition of the name attribute. Because we’ll be dealing with multiple input fields, it’s important to note which of them is actually being modified and the name attribute helps with that. The value of the name attribute has to be the same with the state values declared in the constructor.

This is also why there’s a change in the handleChange function.

In the code block above, the handleChange function uses the name attribute that was assigned to the different input elements to determine what to do based on the value of the event.target.value

Textarea

The textarea field also works in a similar fashion to that of the input field. The value is set to be in sync with message which is already declared in the state. It also has the name attribute and it’s set to message.

Select

The select element has a value attribute that’s set to be in sync with editor which is already declared in the state. Because it’s a dropdown of options, it’s important to know what’s being selected, that’s why each of the option tags has it’s own value attribute with unique contents.

Checkbox

The implementation of the checkbox element in React forms is a bit different from the others above. Instead of setting the this.state.terms value to the value attribute on the input field, it’s set to the checked attribute. The state value also has to be a boolean value, that means either a truthy or falsy value.

Radio

Implementing the radio element in React forms works in a similar fashion to that of the checkbox above. The radio elements all have the same name attribute but with different value attributes, as seen above where the value for the Yes radio is Yes and the value for the No radio is No. The checked attribute is used to set the value of the state to either Yes or No as seen above whenever either of the two is selected.

Using Formik

If you think setting up React forms in the manner above is a bit stressful and worrisome, then I have good news for you. The Formik library helps to make powerful and lightweight forms in React. It gives you the ability to grab and manipulate values, set errors and warnings, customize inputs and many more features that make building forms easy.

Formik keeps track of your form’s state and then exposes it plus a few reusable methods and event handlers (handleChange, handleBlur, and handleSubmit) to your form via props. handleChange and handleBlur work exactly as expected — they use a name or id attribute to figure out which field to update. — https://github.com/jaredpalmer/formik

The Formik component in conjunction with Yup can be used to write awesome form validations. Yup is used for object schema validation and that means it can be used as a validator for when building React forms with formik.

When it comes to the formik API, there are three important APIs to consider and understand:

  1. withFormik(options)
  2. <Field />
  3. <Form />

withFormik(options)

The withFormik(options) allows you to create a higher-order React component class. You can then pass in some props and form handlers in the component based on the supplied options. Let’s take a look at some of the available options that can be in withFormik

handleSubmit

handleSubmit as the name suggests, helps with the form submission in formik. It is automatically passed the form values and any other props wrapped in the component.

mapPropsToValues

mapPropsToValues is used to initialise the values of the form state. Formik transfers the results of mapPropsToValues into updatable form state and makes these values available to the new component as props.values.

validationSchema

This could a function that returns a Yup Schema or an actual Yup schema itself. This helps with validation inside the form.

<Field />

The Field component in Formik is used to automatically set up React forms with Formik. It’s able to get the value by using the name attribute, it uses the name attribute to match up the Formik state and it always set to the input element. That can easily be changed by specifying a component prop.

<Form />

<Form/> is a helper component that helps to save time. It helps to prevent typing out <form onSubmit={props.handleSubmit}/>. All you have to do is wrap in all the form details in a <Form></Form> tag, like the code below:

With those basics understood, let’s see how Formik can be used to build and validate a React form.

Like the controlled components above, a full-fledged form will be built and we’ll then go over the different parts of the form and how it works. The entire code/demo can be viewed with the link below.

In the code block above, the first lines of code are imports. We import React, render from react-dom, some components from formik with a destructuring assignment. Yup which is a JavaScript object schema validator is also imported.

The next block of code is the fat arrow App function with a param of props. The props param is set to an object that contains values that will be used in the app.

All of the details gotten from the form is stored in values, validation errors are stored in errors, touched is a boolean value that checks if an input field is in focus, handleChange helps to perform a certain function whenever there’s a change in an input field and isSubmitting is also a boolean value that’s set to true whenever the submit has been clicked.

The App function also returns a div that contains the JSX markup for the form.

On line 17, the <Form> component is used to encompass the whole code needed for the form. As mentioned above, it helps to prevent typing out <form onSubmit={props.handleSubmit}/>.

I’ll highlight the different Field components and input elements in the Form component and how they work with Formik.

Text Input

The Field component is always to set to the input element. So all that’s left is to define the name attribute so that Formik can automatically get the value.

The line of code above the Field component is simply used for validation purposes. It checks if the input element is in focus with touched.fullname and then checks if there are any errors with errors.fullname, if there are errors, it then shows the custom message in the object schema validator. I’ll touch on the setup for validation later.

Select

As mentioned above, the default state of a Field component is set to input but that can easily be changed by specifying a component prop as seen above. The name attribute is set to editor and has two option elements with different values. The first line of code is also used for validation as explained above.

Radio

For the radio element, we are not able to use the Field component but instead the old fashioned way with input and a type of radio. Both radio options are set to the same name attribute but each radio option has a different value.

Checkbox

The checkbox element here is the Field component with a type of checkbox. The checked event is set to change the value of the newsletter value to either a truthy or falsy value.

Submit Button

Another thing to note is the button element. Formik automatically detects that clicking on the button element at the end of the form signifies the user’s intent to submit all of the form details.

<button disabled={isSubmitting}>Submit</button>

isSubmitting is a submitting state. It’s either set to a truthy or falsy value. When it’s set to true, the button will be disabled while Formik makes a call to the handleSubmit handler.

The DisplayFormikState function is a stateless function that helps to show the raw data and error values in the form via the props.

FormikApp is used to encompass the whole form in the withFormik Higher-order Component (HoC).

The mapsPropsToValues function helps to transform the outer props we defined in the App function earlier into form values. It returns all the values gotten from the form details. The next thing to note is the validationSchema.

The validationSchema is what helps with the form validation. It uses Yup, which is an object schema validator to achieve that.

As you can see above, the name of the value is set to Yup, and then you decide if the value should be a string, number, boolean or date. You can also make sure that the value is required by chaining required() and putting the error message inside the parenthesis.

Yup also allows you to set the minimum length of a string with the min() API. It accepts two values, the number of characters and the error message if the value is not up to that amount.

You can check out the Yup documentation for more API and how you can validate your forms better.

The handleSubmit function is the submission handler, it accepts values (which contains the form details), resetForm which is used to reset all the form values, setErrors which sets the error messages if there are errors and setSubmitting is used to set isSubmitting imperatively.

Our form is then wrapped in the withFormik HoC and FormikApp is rendered on the React App.

With all the code above, you can see how easy, Formik makes building React apps.

Conclusion

In this tutorial, we went through a guide how to build forms with React. I highlighted the fact that React does not ship with a default way of handling forms, instead, you’d have to use the handleChange event to check for changes and at the same time sync with the state. I also explained the basics of Controlled Components in React.

We also used Formik, which is basically a HoC that helps with the building of forms. We went through the different APIs that ships with Formik and also how to use them.

The demo and code for the form built with Controlled Components can be seen on CodeSandbox and that of Formik can be seen here too.


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.

Yomi Eluwande JavaScript Developer. Wannabe Designer and Chief Procrastinator at Selar.co and Worklogs.co

Leave a Reply