How to create generic Forms and Fields components to reduce repetitive code with React and TypeScript? | Part I

Cómo-crear-componentes-genéricos-Forms-y-Fields-reducir-código-repetitivo-React-y-Typescript-Parte-I-Itequia-EN-CAT

How to create generic Forms and Fields components to reduce repetitive code with React and TypeScript? | Part I

Forms are a big topic because they are extremely common in the applications we build. In this article, we’ll learn how to create forms using React-controlled components and look at how to build generic components to help us minimize and optimize code.

componentes-genéricos-Forms-y-Fields-Itequia

Also, client-side validation is critical to the user experience of the forms we build, so we’ll cover that as well. Submitting the form is also a critical consideration. We’ll cover how to handle submission errors as well as success in a generic way.

So, in this series of articles, we are going to cover the following topics:

  • Understanding Controlled Components
  • Reduce boilerplate code with generic components
  • Implementation of validation
  • Form submission

In React, a JavaScript library that we have already seen in previous articles, we can use controlled components to implement a form, a controlled component has its value synchronized with the state in React, through the useState function, which is part of the API of Hooks.

In the example that we will show below, Article.tsx, we have carried out the following actions:

  • Grouped the inputs and the “Save” button in one form, so we can invoke the submit by clicking the “Save” button or pressing the Enter key
  • We assign to the value property of the inputs some states of react (inputNameValue/setInputNameValue and inputSurnameValue/setInputSurnameValue), this is what is known as controlled components, since these elements (input, checkbox, etc) have an internal control of their state, provided by the HTML implementation in the browser itself, an input is capable of maintaining the value entered by the user, however, React’s useState appropriates said state, we are the ones who control the value and rendering of its content
  • We have created a method (handleInputNameChange and handleInputSurnameChange) that is executed every time the change event of an input is fired to constantly have the value of the inputs, storing them in our respective useState
  • We have created a method with the name requiredField to control if the name field is empty, in which case we show an error message

If we do not have a generic component, we have to constantly repeat all this code in each of the screens that need to create forms, states, detect events of value changes in inputs, checkboxes, field validation, and all kinds of code that we save and optimize by creating a generic component.

It is important to point out that for this article we are going to use React and TypeScript together. React defines itself as an open source JavaScript library created by Facebook to build user interfaces, however, when we add to it the ecosystem of available tools, it is a complete solution for the development of web, mobile, desktop and even virtual reality applications.

Why use React?

With React we can split our application into small reusable components, components which can have their own user interface and behavior.

por-que-react-Itequia

React is a progressive library, that is, it increases the integration of components and frameworks based on our needs, so it will adapt perfectly to our project.

What basic knowledge should we have?

Based on the fact that the learning curve in React is lower compared to other frameworks/libraries, it is important to have basic knowledge of HTML, CCSS, and JavaScript and for this particular article, it would be interesting to have a basic knowledge of TypeScript.

Why TypeScript?

TypeScript doesn’t offer the ability to type data, so it gives us more runtime control of all our code.

por-que-Typescript-Itequia

For example:

const myVariableText: string;
const myVariableNumber: number;

Reduce boilerplate code with generic components

As we mentioned before, in this article we will learn how to create generic forms to help us minimize, and reuse in other projects with the consequent increase in productivity and optimize our code thanks to the reuse of our components, all using React and TypeScript.

In this section, we are going to create a generic form component (FormGeneric.tsx) and a generic field component (FieldGeneric.tsx) that will handle the state of the values ​​(inputNameValue/setInputNameValue and inputSurnameValue/setInputSurnameValue), event detection, validation of fields, etc, that we have previously implemented in Article.tsx, this will drastically reduce the code needed to implement a form.

Steps for creating a generic form component:

  • We create a new component with the name FormGenerico
  • We define the interface for the values ​​of the form fields
export interface Values {

  [key: string]: any;

}

In this case, the key will be the name of the field and the value will be the value of the field, for example:

{

name: “input value name”,

surname:”input value surname”

}
  • Include the children property in the said component to be able to render the nested content inside the form (Own content of the component that will use the FormGeneric and the FieldGeneric, in our case ArticuloUsingFormGeneric.tsx)
  • We create useState for the value of the fields, using the interface created previously (Values)
const [values, setValues] = useState<Values>({});

We establish the initial state in an empty literal object, values ​​will have all the values ​​of all the inputs, in our example the key/value of the object would be:

{

       name: “input value name”,

surname:”input value surname”

  }

Steps for creating the FieldGeneric component

The FieldGeneric component is still any element that can be part of a form, Input, Select, TextArea, Checkbox, etc. As well as the set of said elements that are used in turn as components in this FieldGenerico, in the end, the essence is the same, the more components we have that can be reused, the more productive, the better code quality and the more control over our application we will have.

The steps to follow are:

  • We create a new field with the name FieldGeneric
  • We create the following component properties:
interface Props {

 name: string;

 label?: string;

 type?: 'Text' | 'TextArea';

}
  • We render the component FieldGeneric with the label
  • We render the controls according to their type
{(type === 'Text') && (

            <input

              type={type.toLowerCase()}

              id={name}

              ....

          )}

          {type === 'TextArea' && (

            <textarea

              id={name}...

         

Sharing state with React Context

It is important to note that the state of the values ​​of a field is found in the FormGeneric component, but it is presented and changed in the FormGeneric component.

Thanks to React Context, FormGeneric can share its state with FieldGeneric, with React context it is possible to pass data through the component tree, without having to manually pass properties at each level, in this case, FormGeneric provides and FieldGeneric consumes the values ​​it presents FormGeneric.

Creating context in FormGeneric

At this point, we will focus on who provides, and who is the source of the values ​​that will be consumed by the FieldGeneric generic component.

  • We add the createContext function
import { FC, useState, createContext } from 'react';
  • We create an interface for our context
interface FormContextProps {

  values: Values;

  setValue?: (fieldName: string, value: any) => void;

}

Our context will contain the values ​​of the FormGeneric and the function to update them

  • We create the context
export const FormContext = createContext<FormContextProps>({

  values: {},

});

Creating context provider

Now that we have created the context in FormGeneric, we are going to use its Provider component.

<FormContext.Provider

      value={{

        values,

        setValue: (fieldName: string, value: any) => {

          setValues({ ...values, [fieldName]: value });

        },

        ...

In this way, we use its Provider component, to give access to said context to its children components (FieldGeneric), so that they can be consumed.

And here ends the first part of this section of articles on how to create generic components with React and TypeScript to reduce repetitive code in our projects.

We are waiting for you in the second part, in which we will detail how to consume a FormGeneric Context, how to implement it, and in which we will have a complete example.

Hugo Pasctual – Software Developer at Itequia