I made a simple validation library for React.
Tags: react, validation
Published: 2021-10-10 13:23:09

In developing a web application using React, I encountered the problem of how to validate a Form.

I've been comparing and reviewing various libraries such as `React Hook Form` and `Formik`.
However, I decided to create my own library because I didn't need such a big feature and just wanted to be able to do simple validation.

https://github.com/wattanx/react-svl

## Features I wanted.

What I wanted as a feature of the validation library was

- Ability to specify validation rules
- Use Controlled Components
- Required check, minimum/maximum value check, maximum/minimum character length check

## Usage

Install it first.

```
npm install react-svl
```

or

```
yarn add react-svl react
```

We will use Chakra UI to explain.

```tsx
import { Box } from '@chakra-ui/layout';
import { Input } from '@chakra-ui/input';
import { FormControl, FormErrorMessage, FormLabel } from '@chakra-ui/form-control';
import { useForm } from 'react-svl';

export const ValidationExample = () => {
  const { errors, values, setFieldValue, validateField } = useForm({
    initialValues: {
      FirstName: '',
      Password: '',
    },
    validationRules: {
      FirstName: {
        isRequired: true,
      },
      Password: {
        minLength: 8,
      },
    },
  });
  return (
    <Box>
      <FormControl isInvalid={errors.FirstName.isInValid}>
        <FormLabel>First Name</FormLabel>
        <Input
          value={values.FirstName}
          onChange={(e) => setFieldValue('FirstName', e.currentTarget.value)}
          onBlur={() => validateField('FirstName')}
        />
        <FormErrorMessage>{errors.FirstName.isRequired?.message}</FormErrorMessage>
      </FormControl>
      <FormControl isInvalid={errors.Password.isInValid}>
        <FormLabel>Password</FormLabel>
        <Input
          value={values.Password}
          onChange={(e) => setFieldValue('Password', e.currentTarget.value)}
          onBlur={() => validateField('Password')}
        />
        <FormErrorMessage>{errors.Password.minLength?.message}</FormErrorMessage>
      </FormControl>
    </Box>
  );
};
```

One Custom Hooks is imported from `react-svl`.

`useForm` is a Hooks that provides functions needed for validation and setting validation rules.

## Details

### Setting up validation rules

First, we will set the validation rules and initial values.

We will set the initial values in `initialValues` and the validation rules in `validationRules`.
This was implemented to satisfy the feature we wanted, `to be able to specify validation rules`.


```tsx
const { errors, values, setFieldValue, validateField } = useForm({
  initialValues: {
    FirstName: "",
    Password: "",
  },
  validationRules: {
    FirstName: {
      isRequired: true,
    },
    Password: {
      minLength: 8,
    },
  },
});
```

The following types have been created so that they can be set to type safe on input.

```tsx
export type UseFormProps<T> = {
  validationRules: ValidationRule<T>;
  initialValues: T;
};

export function useForm<T extends { [key: string]: any }>({
  initialValues,
  validationRules,
}: UseFormProps<T>);
```

The following items can be set as validation rules.

| property   | type                           | description                               |
| :--------- | :----------------------------- | :---------------------------------------- |
| isRequired | boolean                        | Required or not                           |
| max        | number                         | Maximum value                             |
| min        | number                         | Minimum value                             |
| maxLength  | number                         | Maximum text length                       |
| minLength  | number                         | Minimum text length                       |
| validate   | (value: T[keyof T]) => boolean | Custom rule(true: Error、false: no error) |

### Change State

You can use `setFieldValue` to change the State.

```tsx
<Input value={values.FirstName} onChange={(e) => setFieldValue('FirstName', e.currentTarget.value)} />
```

### Execute Validate

Using `validateField` will execute the validation.

```tsx
<Input
  value={values.FirstName}
  onChange={(e) => setFieldValue('FirstName', e.currentTarget.value)}
  onBlur={() => validateField('FirstName')}
/>
```

### Default Error Message


You can use `errors.property_name.rules.name.message`.

```tsx
<FormControl isInvalid={errors.FirstName.isInValid}>
  <FormLabel>First Name</FormLabel>
  <Input
    value={values.FirstName}
    onChange={(e) => setFieldValue("FirstName", e.currentTarget.value)}
    onBlur={() => validateField("FirstName")}
  />
  <FormErrorMessage>{errors.FirstName.isRequired?.message}</FormErrorMessage>
</FormControl>
```
It also sets the default message for each validation rule, as shown below. (None in the case of custom rule)

| Rule       | Error Message                                                                       |
| :--------- | :---------------------------------------------------------------------------------- |
| isRequired | `{property name}` is required.                                                      |
| max        | `{property name}` must be less than or equal to `{Maximum value}`.                  |
| min        | `{property name}` must be greater than or equal to `{Minimum value}`.               |
| maxLength  | `{property name}` must be less than or equal to `{Maximum text length}` characters. |
| minLength  | `{property name}` must be `{Minimum text length}` characters or more.               |

### Demo

The source code introduced so far looks like the following when actually run.

@[codesandbox](https://codesandbox.io/embed/competent-bas-gv4o9?fontsize=14&hidenavigation=1&theme=dark)