Validate Form during Browser Idle for Performance Optimization
Tags: webdev, javascript, react, typescript
Published: 2023-01-30 14:04:42

When do you validate the value entered by the user in the input form? 🤔
Perhaps, you check it at real timing, or just after tapping a button like "Submit" .

I suggest new way of form validation which uses `requestIdleCallback` .

https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback

By checking the data entered by the user while the browser is idle, the result of the validation is already available when the user taps a button like "Confirm" .
It makes us faster response to button taps.

This article introduces the example using [`idle-task`](https://github.com/hiroki0525/idle-task) (v3.3.1) which wraps `requestIdleCallback` conveniently.

## Example of React

This is the example of react hooks.

```react
import { useRef, useEffect } from "react";
import { setIdleTask, forceRunIdleTask, cancelIdleTask } from "idle-task";

export default function useValidateWhenIdle(
  input: string,
  validate: (input: string) => boolean
) {
  const idleTaskIdRef = useRef(NaN);

  useEffect(() => {
    const validateTask = () => validate(input);
    idleTaskIdRef.current = setIdleTask(validateTask);
    return () => {
      cancelIdleTask(idleTaskIdRef.current);
    };
  }, [input, validate]);

  return () => forceRunIdleTask(idleTaskIdRef.current);
}
```

The component that uses this hooks is as follows.

```react
import "./styles.css";
import useValidateWhenIdle from "./useValidateWhenIdle";
import { useState, ChangeEventHandler, FormEventHandler } from "react";
import { configureIdleTask } from "idle-task";

configureIdleTask({
  debug: true
});

const validateUserName = (userName: string): boolean => {
  return /^[a-zA-Z]+$/.test(userName);
};

export default function App() {
  const [userName, setUserName] = useState("");
  const validateUserNameWhenIdle = useValidateWhenIdle(userName, validateUserName);
  const handleUserNameChange: ChangeEventHandler<HTMLInputElement> = ({
    target
  }) => {
    setUserName(target.value);
  };
  const handleSubmit: FormEventHandler<HTMLFormElement> = async (event) => {
    event.preventDefault();
    const isValid = await validateUserNameWhenIdle();
    if (isValid) {
      alert(`YourName: ${userName}`);
    } else {
      alert(`${userName} is invalid`);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        FirstName:
        <input type="text" value={userName} onChange={handleUserNameChange} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}
```

First, let's take a closer look at the `useValidateWhenIdle` hooks.

`useValidateWhenIdle` takes two arguments.

- `input` : String to be validated
- `validate` ： Function that takes a string to be validated and validates it

The return value is the function which can get validation result.

We will also take a closer look at the internal processing.

```react
  const idleTaskIdRef = useRef(NaN);

  useEffect(() => {
    const validateTask = () => validate(input);
    idleTaskIdRef.current = setIdleTask(validateTask);
    return () => {
      cancelIdleTask(idleTaskIdRef.current);
    };
  }, [input, validate]);
```

The `setIdleTask` of `idle-task` makes the argument function run while the browser is idle.
`setIdleTask(validateTask)` will cause `validateTask`, a function that validates user input values, to be executed while the browser is idle.

The return value of `setIdleTask` is an ID.
This ID can be used to cancel the execution of an idle function or to retrieve the result of an idle function.

We uses `useRef` to keep track of the ID.
Then, `useEffect` makes us ensure that validation is performed while the browser is idle each time a user-entered value is changed.
In the `useEffect` cleanup function, we use `cancelIdleTask` to cancel the target process if it has not yet been executed.

```react
  return () => forceRunIdleTask(idleTaskIdRef.current);
```

The result of a function registered with `setIdleTask` can be retrieved with `forceRunIdleTask`.
If the function has already been executed while the browser is idle, the result is returned; if it has not yet been executed, it is executed immediately.

Next, let's take a closer look at the components on the side that use `useValidateWhenIdle` hooks.

```react
import "./styles.css";
import useValidateWhenIdle from "./useValidateWhenIdle";
import { useState, ChangeEventHandler, FormEventHandler } from "react";
import { configureIdleTask } from "idle-task";

configureIdleTask({
  debug: true
});

const validateUserName = (userName: string): boolean => {
  return /^[a-zA-Z]+$/.test(userName);
};

export default function App() {
  const [userName, setUserName] = useState("");
  const validateUserNameWhenIdle = useValidateWhenIdle(userName, validateUserName);
  const handleUserNameChange: ChangeEventHandler<HTMLInputElement> = ({
    target
  }) => {
    setUserName(target.value);
  };
  const handleSubmit: FormEventHandler<HTMLFormElement> = async (event) => {
    event.preventDefault();
    const isValid = await validateUserNameWhenIdle();
    if (isValid) {
      alert(`YourName: ${userName}`);
    } else {
      alert(`${userName} is invalid`);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        FirstName:
        <input type="text" value={userName} onChange={handleUserNameChange} />
      </label>
      <input type="submit" value="Submit" />
    </form>
  );
}
```

This is an example of a basic React form implementation.
What are the key points?

```react
const validateUserNameWhenIdle = useValidateWhenIdle(userName, validateUserName);
```

The `useValidateWhenIdle` appears.
The first argument is a string to be validated, and the second argument is a function that defines the validation logic.
And the return value is function which can get the result of 
the validation.
It is used when the Submit button is pressed as follows.

```react
  const handleSubmit: FormEventHandler<HTMLFormElement> = async (event) => {
    event.preventDefault();
    const isValid = await validateUserNameWhenIdle();
    if (isValid) {
      alert(`YourName: ${userName}`);
    } else {
      alert(`${userName} is invalid`);
    }
  };
```

When the Submit button is pressed, we get the validation result by using `async/await`.

The page implemented with the code introduced so far can be viewed at the following URL.

https://kzjljm.csb.app/

Let's see if the validation actually takes place while the browser is idle!
By adding the following code, the results of the validation function execution will be output to the browser's console.

```javascript
import { configureIdleTask } from "idle-task";

configureIdleTask({
  debug: true
});
```

If you put text in the input area, you will see that validation is taking place during idle.

![Console Log of validation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/16vp6nnml749wgf384d8.png)

Measurements taken from the Performance tab of the Chrome Developer Tools show that functions are running while the browser is idle.

![Measurements taken from the Performance tab of the Chrome Developer Tools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xu4xfneofrb0fei2apxe.png)

You can see the example in CodeSandbox.

{% embed https://codesandbox.io/embed/nostalgic-pateu-kzjljm?fontsize=14&hidenavigation=1&theme=dark %}

## Conclusion

I introduced how to validate form during browser idle by using `idle-task` .

https://github.com/hiroki0525/idle-task

This article will also help you to optimize your website.

https://dev.to/nuko_suke/improve-responsiveness-to-user-interactions-4a3p

If you found this helpful, please help someone else by sharing it or tagging someone in the comments!

Thanks!