skip to Main Content

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:

enter image description here

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

  1. What am I doing wrong?
  2. What is the most reliable way to get the desired behavior?

2

Answers


    1. Your logic revalidates only on submit, if you want it to revalidate on any change whatsoever to the form values then you need to run your validator any time a change is made.

    That means anytime the formData state changes you also need to propagate changes based on this new formData to the errors state.

    1. This is an use case for reducers, that is if you want to implement an arguably "clean" solution at the expense of a bit more complexity.

    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:

    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({});
    
      const handleChange = (event) => {
        const { name, value } = event.target;
        setFormData((prevFormData) => ({ ...prevFormData, [name]: value }));
      };
    
      useEffect(() => {
    
        if (!validator.setData(formData).validate()) {
          setErrors(validator.errors());
        }
      }, [formData]);
    
      return (...)
    }
    

    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.

    Login or Signup to reply.
  1. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search