Telerik blogs
ReactT2_1200x303

Building forms in React can be a hard and repetitive process. That is why form libraries are there to help out. In this post, I discuss the challenges a form library should aim to solve.

Building forms in React can easily become a hard and repetitive process when dealing with forms with complex logic. You have to deal with the form data, validate it, configure when and how to show the error message for invalid inputs, and also be able to reset the form to its initial state when needed.

The React documentation covers how to create forms in React using controlled and uncontrolled components, but you’ll quickly realize that you need a simple and efficient way to build forms when you start dealing with large and complex forms in your projects.

However, there are different form libraries that you can use to ease the complexities of building forms with React. KendoReact Form is one of them. In this post, I’m going to show you a simple form that’s built without any form library and highlight areas that should be improved when using any form library.

All the pain points I’ll mention can be solved using the KendoReact Form library. After you see the demo we explore in this article, I encourage you to read this previous blog post that shows how succinct it is to build complex forms with KendoReact Form. More details and examples are covered in the React Form Guidelines doc.

The Demo

Here’s a simple form to collect information about a user.

The form collects the user’s name, email and a few other pieces of data. The form validates the name, email and date of birth fields. Let’s discuss some of the pain points with this code.

Form State

You should notice the repeated use of React.useState for setting the initial values for the different input controls, and the onChange handler only calling the respective function to update the state of that input. A React form library should make it easy to do this. It should just deal with the input state and give me all the data when the form is submitting.

When the form submission is being processed, I want to know that this is in progress and disable the submit button. So this should be available out of the box.

It should also be possible to specify values to initialize the form with. This is useful for update forms where the user can edit existing records. After changing some fields, the user may decide they only need to change one field and may not be sure which fields they already edited. In this case, it may be useful to reset the form state and only update the needed one. For this scenario, a form library should provide a means to reset the form.

Validation and Error Messages

Validation is vital for any web form, and the complexity can vary. In the example, you can see how I track the error state using errors and touched state. Those state values are modified in the handleBlur and handleFocus event handlers. A form library should provide an easy way to specify the validation logic without you having to deal with the low-level details of Form, Focus and Touch events. It should provide form-level and field-level validation.

The validation logic and complexity depend on the use case. In some scenarios, I may want to use a schema validation library like Yup or use a custom logic, and a form library should provide easy integration for that. This could be implemented by passing a prop to the component. The prop could accept a validation function which returns an error string or object that’ll be used to validate the inputted data when necessary.

It should also be possible for the library to display the error messages without you declaring components for how they should be rendered. This feature can be customizable by allowing you to specify a custom style or class, or provide a simple means to specify a custom-styled component that will be used for displaying them. This gives a balance between giving the library control over how it renders the error messages for prototype or demo purposes, and customizing how you want it to be rendered to fit your UX pattern.

While I believe strong user experience and accessibility are matters that the developer is responsible to uphold, a good React form library will help you adhere to UX best practices.

Custom Components

The sample form has a date input for the date of birth. As you may know, different browsers can render this input differently. You’ll want to create or use a custom component that will look and behave the same irrespective of the browser. You may choose to use any UI library to display a custom date and time input, and a form library should include support for custom components.

Another example of where a custom component can be useful is if you have a form to collect shipping and credit card details. You may want to implement the credit card data collection flow using a component that is designed to look like a physical card.

A form for a credit card displayed like a physical credit card. As the user types, the # symbol changes with a bounce. The first four numbers of the card fill in, then the next eight fill in with an asterisk, and the last four fill in with numbers. Then the form goes to the card holder’s name.

You could add validation to it and display the error message beside the card, while displaying the error message for other text fields related to the shipping below the respective fields.

A form library should make it possible for you to add this component to the form and have its state handled by the form library, the same way other fields are handled. It should provide the form state, validation message and other data necessary for the component as render props or using a React hook.

The KendoReact Form library provides support for custom components. You can use any of the KendoReact form inputs or components from any other UI component library.

Syntax and Performance

Another important feature a React form library should focus on is syntax and performance. While aiming to reduce code repetition and ease state and error management, it’s very likely that a library can be implemented in a way that negatively affects the performance of your app.

Some form libraries provide a higher order component (HOC) that you would wrap your form in, and then get access to props that you may need to connect explicitly to your form and field event handlers. Some might provide a HOC but you don’t need to explicitly connect the props to the event handlers.

For example, take this imaginary form library that requires you to explicitly pass event handlers from the HOC to each field.

const Example = () => (
  <div>
    <h1>A Form</h1>
    <FormHOC
      initialValues={{ email: '', password: '' }}
      onSubmit={(values) => {
          alert(JSON.stringify(values, null, 2));
      }}
    >
      {({
        values,
        errors,
        touched,
        handleChange,
        handleBlur,
        handleSubmit,
        /* and other props */
      }) => (
        <form onSubmit={handleSubmit}>
          <input
            type="email"
            name="email"
            onChange={handleChange}
            onBlur={handleBlur}
            value={values.email}
          />

          <input
            type="password"
            name="password"
            onChange={handleChange}
            onBlur={handleBlur}
            value={values.password}
          />
          
          <button type="submit">
            Submit
          </button>
        </form>
      )}
    </FormHOC>
  </div>
);

There’s too much boilerplate code in the code above. If you miss handling the onBlur event for an input, that could lead to undesirable behavior. That is why the syntax should reduce such boilerplate code, and also allow you to use such explicit mechanisms when desired.

Also, the example above would cause the form to re-render each time a field is updated. Too many re-renders can affect your app if it’s a large form, or if the device cannot handle too many re-renders within a short period of time. The library should be able to isolate component re-renders, such that only the necessary field is updated and re-rendered when necessary.

Conclusion

Building forms in React can be a hard and repetitive process. You have to deal with the form data, validate it, configure when and how to show the error message for invalid inputs, and also be able to reset the form to the initial state. In such a situation, you’ll want to use a form library to help out.

When doing so, there are various features you should use to evaluate a form library. They are:

  1. Form state management
  2. Validation
  3. Integration with custom component and third-party libraries
  4. Syntax
  5. Performance

By using the KendoReact Form, you can simplify your state management, implement form validation, and easily bring in custom components, such as additional KendoReact form controls. The KendoReact Form is part of the KendoReact UI library for React, which contains 90+ similarly handy components.

Now that you’ve seen firsthand the issues with building a form without a library, you can see just how clean it can be to use KendoReact Form in this blog post on how to build forms in React.

See also this guideline for how to build accessible and performant forms.

References


Peter Mbanugo
About the Author

Peter Mbanugo

Peter is a software consultant, technical trainer and OSS contributor/maintainer with excellent interpersonal and motivational abilities to develop collaborative relationships among high-functioning teams. He focuses on cloud-native architectures, serverless, continuous deployment/delivery, and developer experience. You can follow him on Twitter.

Related Posts

Comments

Comments are disabled in preview mode.