Author avatar

Gaurav Singhal

How to Input Validation setTimeout in Reactjs

Gaurav Singhal

  • Nov 15, 2019
  • 8 Min read
  • 18,864 Views
  • Nov 15, 2019
  • 8 Min read
  • 18,864 Views
Web Development
React

Introduction

You might have had the experience of typing something into an input field and suddenly seeing errors all over the input. It's a terrible user experience—something you definitely don't want for users of your own web application.

In this guide, I'll address this prevalent issue and show you a way to delay the error messages using the setTimeout function.

An Example

For this guide, I'll create a very minimal sign-in form that will have two input fields: email and password.

Our component state would look as follows:

1this.state = {
2  values: {
3    email: "",
4    password: ""
5  },
6  errors: {
7    email: "",
8    password: ""
9  }
10};
jsx

We will set the error state respectively for each input field.

Validation Functions

The email and password validation functions are as follows:

1validateEmail = email => {
2  const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
3  if (!re.test(String(email).toLowerCase()))
4    this.setErrors({ email: "Email is invalid" });
5  else this.setErrors({ email: "" });
6};
7
8validatePassword = password => {
9  if (password.length < 8)
10    this.setErrors({ password: "Password must have at least 8 characters" });
11  else this.setErrors({ password: "" });
12};
jsx

In the validateEmail() function, we use a regex to test the email entered by the user, and in the validatePassword() function we check if the password entered by the user has at least 8 characters. If the validations fail, we will set the error state for the respective field and display it.

1setErrors = error =>
2  this.setState({
3    errors: { ...this.state.errors, ...error }
4  });
jsx

Event Handlers

We will validate the input fields when the input changes or loses focus. To simplify the input handling, we will write a single onChange handler for both the inputs.

1handleInputChange = e => {
2  if (e.target.name === "email") {
3    this.validateEmail(e.target.value);
4  }
5  if (e.target.name === "password") {
6    this.validatePassword(e.target.value);
7  }
8  this.setState({
9    values: { ...this.state.values, [e.target.name]: e.target.value }
10  });
11};
jsx

In the onBlur handler, we will call the validate function for the respective fields as follows:

1// ...
2<input
3  type="email"
4  name="email"
5  id="email"
6  value={this.state.values.email}
7  onChange={this.handleInputChange}
8  onBlur={e => this.validateEmail(e.target.value)}
9  title="Email"
10  required
11/>
12// ...
13<input
14  type="password"
15  name="password"
16  id="password"
17  value={this.state.values.password}
18  onChange={this.handleInputChange}
19  onBlur={e => this.validatePassword(e.target.value)}
20  title="password"
21  required
22/>
23// ...
jsx

At this point, the validations would be done instantaneously, so we need to delay it by at least 800ms.

Delay with setTimeout

We will call the setErrors() function in the callback of the setTimeout() function.

The setTimeout() function accepts the first parameter as a function to be executed after a specific duration, and the second parameter is the time duration in milliseconds.

So our updated validation functions would be as follows:

1validateEmail = email => {
2  const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
3  if (!re.test(String(email).toLowerCase()))
4    setTimeout(() => this.setErrors({ email: "Email is invalid" }), 800);
5  else this.setErrors({ email: "" });
6};
7
8validatePassword = password => {
9  if (password.length < 8)
10    setTimeout(
11      () =>
12        this.setErrors({
13          password: "Password must have at least 8 characters"
14        }),
15      800
16    );
17  else this.setErrors({ password: "" });
18};
jsx

Complete Code

index.js file

1import React, { Component } from "react";
2import ReactDOM from "react-dom";
3
4import "./styles.css";
5
6class SignInForm extends Component {
7  constructor(props) {
8    super(props);
9    this.state = {
10      values: {
11        email: "",
12        password: ""
13      },
14      errors: {
15        email: "",
16        password: ""
17      }
18    };
19  }
20
21  submitForm = async e => {
22    e.preventDefault();
23    if (
24      this.state.errors.email.length > 0 &&
25      this.state.errors.password.length > 0
26    )
27      return false;
28    console.log(this.state);
29  };
30
31  validateEmail = email => {
32    const re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
33    if (!re.test(String(email).toLowerCase()))
34      setTimeout(() => this.setErrors({ email: "Email is invalid" }), 800);
35    else this.setErrors({ email: "" });
36  };
37
38  validatePassword = password => {
39    if (password.length < 8)
40      setTimeout(
41        () =>
42          this.setErrors({
43            password: "Password must have at least 8 characters"
44          }),
45        800
46      );
47    else this.setErrors({ password: "" });
48  };
49
50  setErrors = error =>
51    this.setState({
52      errors: { ...this.state.errors, ...error }
53    });
54
55  handleInputChange = e => {
56    if (e.target.name === "email") {
57      this.validateEmail(e.target.value);
58    }
59    if (e.target.name === "password") {
60      this.validatePassword(e.target.value);
61    }
62    this.setState({
63      values: { ...this.state.values, [e.target.name]: e.target.value }
64    });
65  };
66
67  render() {
68    return (
69      <div>
70        <form onSubmit={this.submitForm}>
71          <div className="input-group">
72            <label htmlFor="email">E-mail Address</label>
73            <input
74              type="email"
75              name="email"
76              id="email"
77              value={this.state.values.email}
78              onChange={this.handleInputChange}
79              onBlur={e => this.validateEmail(e.target.value)}
80              title="Email"
81              autoComplete="off"
82              required
83            />
84            <p class="error">{this.state.errors.email}</p>
85          </div>
86          <div className="input-group">
87            <label htmlFor="password">Password</label>
88            <input
89              type="password"
90              name="password"
91              id="password"
92              value={this.state.values.password}
93              onChange={this.handleInputChange}
94              onBlur={e => this.validatePassword(e.target.value)}
95              title="password"
96              required
97            />
98            <p class="error">{this.state.errors.password}</p>
99          </div>
100          <button type="submit">Sign In</button>
101        </form>
102      </div>
103    );
104  }
105}
106
107function App() {
108  return (
109    <div className="App">
110      <h1>Sign In To Your Account</h1>
111      <SignInForm />
112    </div>
113  );
114}
115
116const rootElement = document.getElementById("root");
117ReactDOM.render(<App />, rootElement);
jsx

styles.css

1.App {
2  font-family: sans-serif;
3}
4
5.input-group {
6  margin-bottom: 10px;
7}
8
9.input-group label {
10  display: block;
11  margin-bottom: 5px;
12}
13
14button {
15  border: none;
16  padding: 8px 24px;
17}
18
19.message {
20  margin-top: 20px;
21  font-weight: 600;
22}
23
24.error {
25  color: red;
26  font-size: 14px;
27}
css

Conclusion

In this guide, we looked at how we can tackle a common UX issue on input validations to enhance the usability of forms in web applications.

That's it from this guide. I hope you liked it. For more other tips, refer to my other guides on React.