Forms are a big topic because they are extremely common in the applications we build. In this article, we’ll learn how to create generic forms using React-controlled components and look at how to build generic components to help us minimize and optimize code.
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:
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:
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.
With React we can split our application into small reusable components, components which can have their own user interface and behavior.
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.
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.
TypeScript doesn’t offer the ability to type data, so it gives us more runtime control of all our code.
For example:
const myVariableText: string;
const myVariableNumber: number;
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.
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”
}
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”
}
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:
interface Props {
name: string;
label?: string;
type?: 'Text' | 'TextArea';
}
{(type === 'Text') && (
<input
type={type.toLowerCase()}
id={name}
....
)}
{type === 'TextArea' && (
<textarea
id={name}...
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.
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.
import { FC, useState, createContext } from 'react';
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
export const FormContext = createContext<FormContextProps>({
values: {},
});
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.