React Form Validation with Informed

Pieces 🌟 - Nov 8 '22 - - Dev Community

Stylized image of several forms.

Form validation is an effective way to control the type of values that are provided in the proper format, preventing security vulnerabilities, malicious users, and entering incorrect data. It guarantees the data submitted matches the requirement. There are many validation frameworks/libraries that are used to handle React form validation in web applications; Informed is one such tool.

What is Informed?

Informed is a React form validation tool used to create robust forms. It’s a simple framework that enables you to add custom inputs, dynamic forms, and multi-step forms to your application while making it simple to specify input error messages. It resolves the hassle of handling form state.

Getting Started With Informed

This section will walk you through integrating the Informed React validation library into your application, along with validating fields and creating custom inputs. In addition, we'll create a React application and install the Informed library by performing either of the following commands:

yarn add informed
Enter fullscreen mode Exit fullscreen mode

or

npm install informed
Enter fullscreen mode Exit fullscreen mode

After installing the library, we’ll modify our App.css file by pasting the code block below:

* {
overflow-x: hidden;
}

.App {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #fff;
  color: #000;
  min-height: 100vh;
  width: 100vw;
  padding: 20px;
}

form {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: flex-start;
  width: 700px;
  padding: 15px;
  box-shadow: 10px 10px 15px lightblue;
  border: 1px solid #eee;
  border-radius: 2px;
}

div {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 5px;
}

input {
  width: -webkit-fill-available;
  padding: 5px 10px;
  margin: 5px 0;
}

button {
  border: none;
  margin-top: 10px;
  padding: 10px 25px;
  border-radius: 5px;
  color: #fff;
  background-color: #000;
  cursor: pointer;
}

span {
  color: #ff0000;
}
Enter fullscreen mode Exit fullscreen mode

React Form Validation with Informed

In this section, we’ll go over how to include Informed in your application. By default, the Informed library features native DOM input elements that are controlled by the React form library. This makes it easy to retrieve and manipulate form values; it handles everything related to the form state. Using the code block below, we’ll alter the App.js component:

import { Form, Input, Debug } from 'informed';
import './App.css';


const App = () => (
 <div className="App">
     <Form>
      <div>
       <Input className="input" name="name" label="Name" placeholder="Elon" />
       <Input
        className="input"
        name="age"
        type="number"
        label="Age"
       />
       <Input
        className="input"
        name="phone"
        label="Phone"
        formatter="+234 (###)-###-####"
       />
      <button type="submit">Submit</button>
     </div>
     <Debug/>
    </Form>
  </div>
);

export default App;
Enter fullscreen mode Exit fullscreen mode

The code block above demonstrates how components imported from the Informed library are simply implemented. It’s critical to note the Debug component; this component displays the whole state of your form in real time, allowing for easier debugging. Also, the formatter prop shows you how to format input values. This can be added to text inputs as well.

An example of React form validation.
Basic Informed

Validation Hooks

The Informed library has a variety of hooks that help with various parts of managing simple to complex React forms in your application. We'll be looking at it in this section:

useFormState

The useFormState hook from the Informed library gives you access to the form state properties:

import { Form, Input, useFormState } from 'informed';

const ComponentUsingFormState = () => {
 const formState = useFormState();
 return (
  <pre>
   <code>{JSON.stringify(formState.values, null, 2)}</code>
  </pre>
 );
};

const App = () => (
 <div className="App">
  <Form>
  <div>
   <Input name="name" label="Name:" />
   <Input name="occupation" label="Occupation:" />
   <button type="submit">Submit</button>
  </div>
  <div>
   <h5>Component using formState:</h5>
   <ComponentUsingFormState />
  </div>
  </Form>
 </div>
);
Enter fullscreen mode Exit fullscreen mode

In the code block above, in ComponentUsingFormState, you can access the form values by using the dot notation formState.values:

formState output with Informed.
formState output

useFormApi

The useFormApi hook from the Informed library allows you to gain access to the form API functions. You can alter the value of an input using the functions that formApi provides for React form validation:

...

import { Form, Input, useFormApi } from "informed";

const RandomSetterButton = () => {
 const formApi = useFormApi();
 return (
  <button
   type="button"
   onClick={() =>
    formApi.setValue(
     "name",
     Math.floor(Math.random() * Math.floor(Number.MAX_SAFE_INTEGER))
    )
   }
  >
   Random
   </button>
 );
};

const SetValuesButton = () => {
 const formApi = useFormApi();
 return (
  <button
   type="button"
   onClick={() => formApi.setValues({ name: 'Asta', age: 26, color: "Green" })}
  >
   All
  </button>
 );
};

const SetTheseValuesButton = () => {
 const formApi = useFormApi();
  return (
   <button
    type="button"
    onClick={() => formApi.setTheseValues({ age: 27, color: "Yellow" })}
   >
    Age & Color
   </button>
  );
};

const App = () => (
 <Form onSubmit={({ values }) => window.alert(JSON.stringify(values, null, 2))}>
  <div>
   <Input name="name" label="First Name:" />
   <Input name="age" label="First Name:" type="number"  />
   <Input name="color" label="Favorite Color:" />
   <RandomSetterButton />
   <SetValuesButton />
   <SetTheseValuesButton />
   <button type="submit">Submit</button>
  </div>
 </Form>
);

...
Enter fullscreen mode Exit fullscreen mode

As shown in the code block above, the RandomSetterButton component uses setValue to target a single input. In this case, the name input is targeted, and the value is set to a random number. The SetTheseValuesButton component uses the setTheseValues function to change the values of name, age, and color inputs. Also, the SetValuesButton component uses setValues to set the input values of both age and color to different values. One of the important useFormApi functions is the onSubmit function. This gives you access to the form values, which can be used as payload to an endpoint.

React form validation example.
useFormApi output

useFieldApi

The useFieldApi hook gives you access to the field API functions using a dot notation:

...

import { Form, Input, useFieldApi } from "informed";

const ComponentUsingFieldApi = () => {
 const fieldApi = useFieldApi("name");
 return (
  <button
   type="button"
   onClick={() =>
    fieldApi.setValue(
     ' Math.floor(Math.random() * Math.floor(Number.MAX_SAFE_INTEGER))'
    )
   }
  >
   Random
  </button>
 );
};

const App = () => (
 <Form>
  <div>
   <Input name="name" label="Name:" initialValue="Joe" />
   <button type="submit">Submit</button>
   <h5>Component using fieldApi:</h5>
  <ComponentUsingFieldApi/>
  </div>
 </Form>
);

...
Enter fullscreen mode Exit fullscreen mode

The name field having the initialValue Meliodas, changes to a random number when the random button is clicked:

useFieldApi output with Informed.
useFieldApi output

useFieldState

The useFieldState hook gives you access to the field state attributes:

...

import { Form, Input, useFieldState } from 'informed';

const ComponentUsingFieldState = ({ name }) => {
 const fieldState = useFieldState(name);
 return (
  <>
   <h5>Component using fieldState: {name}</h5>
   Render: {Math.random()}
   <pre>
    <code>{JSON.stringify(fieldState, null, 2)}</code>
   </pre>
  </>
 );
};

const App = () => (
 <Form>
  <div>
   <Input name="name" label="Name:" />
   <Input field="age" label="Age:" type="number" />
   <button type="submit">Submit</button>
  </div>
  <div>
   <ComponentUsingFieldState name="name" />
   <ComponentUsingFieldState name="age" />
  </div>
 </Form>
);

...
Enter fullscreen mode Exit fullscreen mode

From the code block above, the name field is passed into the useFieldState hook as an argument, and can be used to keep track of every name attribute in the form state:

useField State example with Informed.
useFieldState

Form Validation Methods

Simple Validation

Informed forms provide the validate prop to handle field-level validation. You can pass in rules from a function to validate an input field, or mark it as required:

...

import { Form, Text } from 'informed';

const validate = (value) => {
 if (!value || value.length < 5)
  return (
   <span>Field must be at least five characters</span>
  )
};

const App = () => {
 return (
  <Form
   onSubmit={({ values }) => window.alert(JSON.stringify(values, null, 2))}>
   <Input name="color" label="Color:" validate={validate} required />
   <Input name="food" label="Food:" validate={validate} />
   <button type="submit">Submit</button>
  </Form>
 );
};

...
Enter fullscreen mode Exit fullscreen mode

An example of simple form validation with Informed.
Simple validation

Validation Control

For more control when validating, passing validateOn props to fields makes Informed more flexible in handling React form validation. You can also choose to control when the error message shows using the showErrorIfError, showErrorIfTouched, and showErrorIfDirty props:

...

const App = () => (
  <div className="App">
  <Form
   onSubmit={({ values }) => window.alert(JSON.stringify(values, null, 2))}
  >
   <div>

    <h4>validateOn="blur" ( default )</h4>
    <Input
     name="username1"
     label="Username1"
     required
     validate={validate}
    />
    <h4>validateOn="change"</h4>
    <Input
     name="username2"
     label="Username2"
     validateOn="change"
     required
     validate={validate}
    />
    <h4>validateOn="change" && showErrorIfDirty</h4>
    <Input
     name="username3"
     label="Username3"
     validateOn="change"
     showErrorIfDirty
     required
     validate={validate}
    />
    <h4>validateOn="change-blur"</h4>
    <Input
     name="username4"
     label="Username4"
     validateOn="change-blur"
     required
     validate={validate}
    />
    <h4>validateOn="change-submit"</h4>
    <Input
      name="username5"
      label="Username5"
      validateOn="change-submit"
      required
      validate={validate}
    />
    <h4>validateOn="blur-submit"</h4>
    <Input
     name="username6"
     label="Username6"
     validateOn="blur-submit"
     required
     validate={validate}
    />
    <h4>validateOn="submit"</h4>
    <Input
     name="username7"
     label="Username7"
     validateOn="submit"
     required
     validate={validate}
    />
    <h4>validateOnMount</h4>
    <Input
     name="username8"
     label="Username8"
     validateOnMount
     required
     validate={validate}
    />
    <h4>validateOnMount && showErrorIfError</h4>
    <Input
     name="username9"
     label="Username9"
     validateOnMount
     showErrorIfError
     required
     validate={validate}
    />
    <button type="submit">Submit</button>

   </div>
   <div>
    <Debug values errors invalid validating />
    <button type="submit">Submit</button>
   </div>
  </Form>
 </div>
);

...
Enter fullscreen mode Exit fullscreen mode

An example of validation control in Informed.
Validation control

Validation Messages

This method aids you in displaying custom error messages for built-in validations. You can achieve this feat by making use of the errorMessage prop:

...

import {
 Form,
 Input,
 Debug
} from 'informed';

const validate = value => {
 if (!value || value.length < 5)
  return 'Field must be at least five characters';
};

const App = () => (
 <Form
  errorMessage={{ required: 'This field is required for your profile!' }}
  onSubmit={({ values }) => window.alert(JSON.stringify(values, null, 2))}>
  <Input
   name="name"
   label="First name:"
   required
   errorMessage="There is a problem with this field!"
  />
  <Input
   name="last"
   label="Last name:"
   required
   errorMessage={{ required: 'Last name is required!' }}
  />
  <Input name="favoriteColor" label="Favorite color:" required />
  <button type="submit">Submit</button>
  <Debug values errors invalid valid />
 </Form>
);

...
Enter fullscreen mode Exit fullscreen mode

Sending validation messages with Informed.
Validation messages

Basic Form Validation

Informed allows you to create custom validation inputs that can be used across your application. Next, we’ll explore the useField and useForm hooks to create a custom form:

...

import { useForm, useField, Debug } from 'informed';

const CustomForm = ({ children, ...rest }) => {
 const { formController, render, userProps } = useForm(rest);

/* --- DON'T FORGET TO CALL THE RENDER METHOD FROM THE HOOK! --- */
return render(
 <form
  {...userProps}
  onReset={formController.reset}
  onSubmit={formController.submitForm}
  onKeyDown={formController.keyDown}>
  {children}
 </form>
 );
};

const CustomInput = props => {
 const { render, informed, fieldState, userProps, ref } = useField({
  type: 'text',
  ...props
 });

const { id, label, ...rest } = userProps;
 const { error, showError } = fieldState;

/* --- DON'T FORGET TO CALL THE RENDER METHOD FROM THE HOOK! --- */
return render(
 <>
  <label htmlFor={id}>{label}</label>
  <input
   {...rest}
   {...informed}
   ref={ref}
   style={showError ? { border: 'solid 1px red' } : null}
  />
   {showError && <small style={{ color: 'red' }}>{error}</small>}
  </>
 );
};

const App = () => (
 <div className="App">
  <CustomForm
   onSubmit={({ values }) => window.alert(JSON.stringify(values, null, 2))} 
  >
   <div>
    <CustomInput
     field="name"
     label="First name:"
     validateOn="change"
     required
     minLength={5}
    />
    <button type="submit">Submit</button>
   </div>
   <Debug values errors />
  </CustomForm>
 </div>
);

...
Enter fullscreen mode Exit fullscreen mode

Using the useField and useForm hooks, a custom form and input component was generated in the code block above. When utilizing either hook, you must always call the render method obtained from both hooks:

Using custom validations in Informed.
Custom Validation

Conclusion

In this article, we explored the Informed library for handling React form validation in applications. We also covered creating custom inputs and custom validation error messages. To see more of what the library offers, check their docs and GitHub repository.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .