5
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.
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:
1 2 3 4 5 6 7 8 9 10
this.state = { values: { email: "", password: "" }, errors: { email: "", password: "" } };
We will set the error state respectively for each input field.
The email
and password
validation functions are as follows:
1 2 3 4 5 6 7 8 9 10 11 12
validateEmail = email => { const re = /^(([^<>()\[\]\\.,;:\[email protected]"]+(\.[^<>()\[\]\\.,;:\[email protected]"]+)*)|(".+"))@((\[[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,}))$/; if (!re.test(String(email).toLowerCase())) this.setErrors({ email: "Email is invalid" }); else this.setErrors({ email: "" }); }; validatePassword = password => { if (password.length < 8) this.setErrors({ password: "Password must have at least 8 characters" }); else this.setErrors({ password: "" }); };
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.
1 2 3 4
setErrors = error => this.setState({ errors: { ...this.state.errors, ...error } });
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.
1 2 3 4 5 6 7 8 9 10 11
handleInputChange = e => { if (e.target.name === "email") { this.validateEmail(e.target.value); } if (e.target.name === "password") { this.validatePassword(e.target.value); } this.setState({ values: { ...this.state.values, [e.target.name]: e.target.value } }); };
In the onBlur
handler, we will call the validate function for the respective fields as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// ... <input type="email" name="email" id="email" value={this.state.values.email} onChange={this.handleInputChange} onBlur={e => this.validateEmail(e.target.value)} title="Email" required /> // ... <input type="password" name="password" id="password" value={this.state.values.password} onChange={this.handleInputChange} onBlur={e => this.validatePassword(e.target.value)} title="password" required /> // ...
At this point, the validations would be done instantaneously, so we need to delay it by at least 800ms.
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
validateEmail = email => { const re = /^(([^<>()\[\]\\.,;:\[email protected]"]+(\.[^<>()\[\]\\.,;:\[email protected]"]+)*)|(".+"))@((\[[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,}))$/; if (!re.test(String(email).toLowerCase())) setTimeout(() => this.setErrors({ email: "Email is invalid" }), 800); else this.setErrors({ email: "" }); }; validatePassword = password => { if (password.length < 8) setTimeout( () => this.setErrors({ password: "Password must have at least 8 characters" }), 800 ); else this.setErrors({ password: "" }); };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
import React, { Component } from "react"; import ReactDOM from "react-dom"; import "./styles.css"; class SignInForm extends Component { constructor(props) { super(props); this.state = { values: { email: "", password: "" }, errors: { email: "", password: "" } }; } submitForm = async e => { e.preventDefault(); if ( this.state.errors.email.length > 0 && this.state.errors.password.length > 0 ) return false; console.log(this.state); }; validateEmail = email => { const re = /^(([^<>()\[\]\\.,;:\[email protected]"]+(\.[^<>()\[\]\\.,;:\[email protected]"]+)*)|(".+"))@((\[[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,}))$/; if (!re.test(String(email).toLowerCase())) setTimeout(() => this.setErrors({ email: "Email is invalid" }), 800); else this.setErrors({ email: "" }); }; validatePassword = password => { if (password.length < 8) setTimeout( () => this.setErrors({ password: "Password must have at least 8 characters" }), 800 ); else this.setErrors({ password: "" }); }; setErrors = error => this.setState({ errors: { ...this.state.errors, ...error } }); handleInputChange = e => { if (e.target.name === "email") { this.validateEmail(e.target.value); } if (e.target.name === "password") { this.validatePassword(e.target.value); } this.setState({ values: { ...this.state.values, [e.target.name]: e.target.value } }); }; render() { return ( <div> <form onSubmit={this.submitForm}> <div className="input-group"> <label htmlFor="email">E-mail Address</label> <input type="email" name="email" id="email" value={this.state.values.email} onChange={this.handleInputChange} onBlur={e => this.validateEmail(e.target.value)} title="Email" autoComplete="off" required /> <p class="error">{this.state.errors.email}</p> </div> <div className="input-group"> <label htmlFor="password">Password</label> <input type="password" name="password" id="password" value={this.state.values.password} onChange={this.handleInputChange} onBlur={e => this.validatePassword(e.target.value)} title="password" required /> <p class="error">{this.state.errors.password}</p> </div> <button type="submit">Sign In</button> </form> </div> ); } } function App() { return ( <div className="App"> <h1>Sign In To Your Account</h1> <SignInForm /> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
.App { font-family: sans-serif; } .input-group { margin-bottom: 10px; } .input-group label { display: block; margin-bottom: 5px; } button { border: none; padding: 8px 24px; } .message { margin-top: 20px; font-weight: 600; } .error { color: red; font-size: 14px; }
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.
5