I am working on a chat app with React 18 and Firebase.
In the srcpagesRegister.jsx
component, I have a form that I validate with Simple Body Validator:
import React, { useState } from "react";
import FormCard from '../components/FormCard/FormCard';
import { make } from 'simple-body-validator';
export default function Register() {
const initialFormData = { firstName: '', lastName: '', email: '', password: '', passwordConfirm: '' };
const validationRules = {
firstName: ['required', 'string', 'min:3', 'max:255'],
lastName: ['required', 'string', 'min:3', 'max:255'],
email: ['required', 'email'],
password: ['required', 'confirmed'],
passwordConfirm: ['required'],
};
const validator = make(initialFormData, validationRules);
const [formData, setFormData] = useState(initialFormData);
const [errors, setErrors] = useState(validator.errors());
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((prevFormData) => ({ ...prevFormData, [name]: value }));
};
const handleSubmit = (event) => {
event.preventDefault();
if (!validator.setData(formData).validate()) {
setErrors(validator.errors());
}
};
return (
<FormCard title="Register">
<form onSubmit={handleSubmit}>
<div className={`mb-2 form-element ${errors.has('firstName') ? 'has-error' : null}`}>
<label for="firstName" className="form-label">First name</label>
<input type="text" id="firstName" name="firstName" value={formData.firstName} onChange={handleChange} className="form-control form-control-sm" />
{ errors.has('firstName') ? (
<p className="invalid-feedback">{errors.first('firstName')}</p>
) : null }
</div>
<div className={`mb-2 form-element ${errors.has('lastName') ? 'has-error' : null}`}>
<label for="lastName" className="form-label">Last name</label>
<input type="text" id="lastName" name="lastName" value={formData.lastName} onChange={handleChange} className="form-control form-control-sm" />
{ errors.has('lastName') ? (
<p className="invalid-feedback">{errors.first('lastName')}</p>
) : null }
</div>
<div className={`mb-2 form-element ${errors.has('email') ? 'has-error' : null}`}>
<label for="email" className="form-label">Email address</label>
<input type="email" id="email" name="email" value={formData.email} onChange={handleChange} className="form-control form-control-sm" />
{ errors.has('email') ? (
<p className="invalid-feedback">{errors.first('email')}</p>
) : null }
</div>
<div className={`mb-2 form-element ${errors.has('password') ? 'has-error' : null}`}>
<label for="password" className="form-label">Password</label>
<input type="password" id="password" name="password" value={formData.password} onChange={handleChange} className="form-control form-control form-control-sm" />
{ errors.has('password') ? (
<p className="invalid-feedback">{errors.first('password')}</p>
) : null }
</div>
<div className={`mb-2 form-element ${errors.has('passwordConfirm') ? 'has-error' : null}`}>
<label for="password_repeat" className="form-label">Confirm Password</label>
<input type="password" id="passwordConfirm" name="passwordConfirm" value={formData.passwordConfirm} onChange={handleChange} className="form-control form-control form-control-sm" />
{ errors.has('passwordConfirm') ? (
<p className="invalid-feedback">{errors.first('passwordConfirm')}</p>
) : null }
</div>
<div className="pt-1">
<button type="submit" className="btn btn-sm btn-success fw-bold">Submit</button>
</div>
</form>
</FormCard>
);
}
If the form is invalid, once a submit is atempted, the validation error messages are displayed as expected:
The problem
If valid data is introduced into an invalid form, the validation error messages and classes do not disappear on input/change, as I believe it should.
The valid fields pass the validation only upon a new click of the submit button while I want the positive feedback to be instant.
Sandbox
There is a sandbox with the code here.
Questions
- What am I doing wrong?
- What is the most reliable way to get the desired behavior?
2
Answers
That means anytime the formData state changes you also need to propagate changes based on this new formData to the errors state.
But the easiest, most straight forward way to achieve this result with minimal changes to your logic would be to move your validation logic in an effect ran when formData changes, such as:
Note how i removed your handleSubmit function, we don’t need it anymore since the effect will run anytime the input changes anyway, which will happen before any submit does.
https://github.com/crazyobject/react-validation-formik-yup
I usually do this using formik and yup.check above repo for code and below is the link for stackblitz.
https://stackblitz.com/edit/validation-react-form-yup-formik?file=src%2FApp.js
Basically we need to handle below two events
Onchange
Onblur