The complete guide to Forms in React

a letter about react forms to me in the future

Forms are very useful in any web application. Unlike angular and angularjs, that gives form validation out of the box. You have to handle forms yourself in React. This brought about many complications like how to get form values, how do I manage form state, how do I validate my form on the fly and show validation messages. There are different methods and libraries out there to help with this but if you are like me that hates dependent on too many libraries, welcome on board, We are going to bootstrap our own form from the ground up.

There are two types of form input in react. We have the uncontrolled input and the controlled input. The uncontrolled input are like traditional HTML form inputs, they remember what you typed. We will use ref to get the form values.

We added the ref="name" to the input tag so that we can access the value with this.refs.name.value when the form is submitted. The downside to this is that you have to “pull” the value from the field when you need it and this can happen when the form is submitted.

The controlled input is when the react component that renders a form also controls what happens in that form on subsequent user input. Meaning, as form value changes, the component that renders the form saves the value in its state.

Of course, another component can handle the form state. The goal is that each time the input changes, the method changeHandler is called and will store the input state. Hence the component always has the current value of the input without needing to ask for it. This means that the form component can respond to input changes immediately; for example

  • in-place feedback, like validation
  • disabling the button unless all fields have valid data
  • enforcing a specific input format

Handling multiple form inputs

In most situations, we are going to have more than one form input. We need a way to capture the input with a method instead of declaring multiple methods to do this. Hence we are going to modify the changeHandler to below:

Because of the way the changeHandler has been modified above, our form input can reference it to update it states dynamically.

The value of the name attribute on each input must be the same with the state name declared in the formControls in the constructor.

Create a TextInput Component

There are different input elements e.g text, email, password, select option, checkbox, date, radio button, etc. I love to create separate custom component for the input elements, let us start with the text input type.

Notice the {…props}, we use this to distribute the props to the input element. We can use the custom text input element like below:

Validating our Custom TextInput

Since we are using the controlled input, we can add more keys to our formControls state to help validate the input. We need the valid property to denote if the input is valid or not, the validationRules contains the list of the rules to be checked before the input is valid.

Our aim is that each time the input changes. We make sure the validationRules for that input is checked for true or false, then update the valid key with the result of the check. We also added the touched property to denote that the user has touched the form input, this will help with displaying validation feedback when the input has been touched. The check will be done in the changeHandler method like below:

The valid is equated to the methodvalidate(value, prevState.formControls[name]).validationRules) which we will use to check if the valid status for a particular control is true or false.

I move the validate method to a separate class then import it. The validate method accepts two parameters, the value and the rules. We loop through the rules and check if each rule is valid, then return true when valid and false when invalid.

Let assume we want to add another validation on name e.g we wantname to be required. All we need do is update the formControl for name validationRules, and write the logic for it in the validator class like below

Then we need to update the validator class to accommodate for the required validator.

We created a custom TextInput, we created a formControl that has a property named name with a validation rules of isRequired and minLength of 3. Below is the component that handles this:

If we click the submit button after filling the TextInput, the formSubmitHandler will console the formControls value like below

valid = true or false

The good thing is that we do not have to wait till the user click submit before we can know if the form input is valid or not. Since it is actually stored in the component state, so, therefore, we can use this to display error message or feedback when the user is typing. We can even disable the submit button until the validation passes.

Displaying error feedback

For us to be able to display error feedback on the input, we need to pass the touched and valid property for that particular input as a prop to it component. We will add the error style based on the valid status and we want to do this only when the input has been touched.

We also need to modify our TextInput component to display the style based on the value props.valid and props.touched.

Please note that you should have added the form-control and control-error style into the App.css.

You should see a screenshot like below if you TextInput is invalid and had been touched.

Disable Submit Button if the form is Invalid

Html 5 has a disabled property on button input, we can equate our formControls property valid status to the disabled property. As long as the formControls is not valid.

The disabled={!this.state.formControls.name.valid} will work fine if we have only one form control but if we need to handle more than one form control, we can set a new property to the state which will keep track of when the validity status of the whole formControl object. So we need to update our state to accommodate for this

We need to update the changeHandler method so we can loop through all form controls valid status, and when valid, update the formIsValid status to true.

With this setup, it will be easier for us to set the disabled property to formIsValid status, and this will handle one or more form object.

Considering other form input type

TEXTAREA: The textarea, email and password will work similar to a text input. We can create a TextArea component.

EMAIL: We can also create an Email component just like the TextInput

PASSWORD: We can also create a Password component just like the TextInput

The email, textarea and password form control will look similar to the text input form input

same for email, password and textarea, text input

SELECT OPTION: The Select Option form control is slightly different to the other form control because we have to accommodate for the select options. It will look like to below

then the Select Option component will look like below

RADIO: The radio input is similar to the select option since it’s only one option that can be selected out of the available options, The form control will be similar to the select option form control. Below is how the radio button component looks like.

Putting it all together, Assuming we want to have an email input, name (TextInput), gender (Select Input), radio input all in a form control. Below is an example of what your component will look like

Thanks for reading.