skip to Main Content

I’m stuck in a problem related to react useState and not sure how to solve it.

Some background. I’ve a register form with validation on blur on each field. The validation works if I blur individual field which is basically setting the error message (using useState) if there’s any error. I’ve called the same validation methods on form submit. So that I don’t have to write a duplicate code.

The problem arise when I submit without invoking the blur methods. It runs all the methods and set the error messages but the useState values are not available. I read some articles that says to use useEffect but then it will either run without any watcher or with watchers. If it runs without any watcher then the ux will be bad and user will be shown errors when they freshly come to the page. Please see image

OR

If we add watchers in our useEffect then validation will be run on each key stroke which I don’t want.

useEffect(()=>{
    handleOnBlur("firstname", firstname);
    handleOnBlur("lastname", lastname);
    handleOnBlurPassword(password);
},[firstname, lastname, password])

I’m looking to solve this without useEffect or some other way in which I don’t compromise the UX.

I’ve created a codesandbox. Please have a look if my i’m not clear in my description

4

Answers


  1. I’m not very experienced with React but I think for this kind of forms it’s best practice to use React useRef hook instead of useState.
    This way the state will not change with every type of the user.
    You can use the useEffect with empty brackets for the mount (first render) and add another useEffect with names and password dependecies for later checks.
    Hope this is helpful 🙂

    Login or Signup to reply.
  2. You can add another validations step on the onSubmit event, or, if the form is submitted without this React event, then manually run validations just before submitting the data to the server, and obviously, if the form is deemed "invalid", do not continue (do not save the form on the server)

    Put your complete form validation step here, instead of those 3 marked lines:

    enter image description here

    Create a function that validates all the fields and returns true if everything is valid, and only then proceed with the actual form submission.

    Login or Signup to reply.
  3. You may use useRef() instead of useState using following syntax :
    const firstName = useRef() and obtain firstname’s values as firstname.current.value.
    Following link might help you : w3schools.com/react/react_useref.asp

    Login or Signup to reply.
  4. According to the defined requirement i have updated the code.
    Please look into it and update the changes as well.

    previous code and need to change : The errors have been set if there is no value, but haven’t set if there is a value.

    This code might be helpful to you.

    thank you so much !

    App.js

    import { useEffect, useState } from "react";
    import "./styles.css";
    
    export default function App() {
      const [firstname, setFirstname] = useState("");
      const [lastname, setLastname] = useState("");
      const [password, setPassword] = useState("");
      const [loading, setLoading] = useState(false);
      const [errorMessages, setErrorMessages] = useState({
        firstname: "",
        lastname: "",
        password: ""
      });
      useEffect(() => {
        handleOnBlur("firstname", firstname);
        handleOnBlur("lastname", lastname);
        handleOnBlurPassword(password);
      }, []);
      const handleOnSubmit = (event) => {
        event.preventDefault();
        setLoading(true);
        handleOnBlur("firstname", firstname);
        handleOnBlur("lastname", lastname);
        handleOnBlurPassword(password);
    
        const hasError = Object.values(errorMessages).find(
          (value) => value.length > 0
        );
        if (!hasError) {
          setTimeout(() => {
            // mocking api
            console.log("Api response here::");
            setLoading(false);
          }, 5000);
        } else {
          setLoading(false);
        }
      };
      const handleOnBlur = (label, value) => {
        if (!value) {
          setErrorMessages((errMsgs) => ({
            ...errMsgs,
            [label]: `Please enter your ${label}`
          }));
        } else {
          setErrorMessages((errMsgs) => ({
            ...errMsgs,
            [label]: ""
          }));
        }
      };
      const handleOnBlurPassword = (value) => {
        if (!value) {
          setErrorMessages((errMsgs) => ({
            ...errMsgs,
            password: `Please enter your password`
          }));
        } else {
          setErrorMessages((errMsgs) => ({
            ...errMsgs,
            password: ""
          }));
        }
      };
    
      return (
        <div className="App">
          <h1>Hello CodeSandbox</h1>
          <h2>Start Registering!!</h2>
          <form onSubmit={handleOnSubmit}>
            <div className="text-input">
              <label htmlFor="firstname" className="">
                First name:
              </label>
              <input
                type="text"
                name="firstname"
                value={firstname}
                onChange={(e) => setFirstname(e.target.value)}
                onBlur={(e) => handleOnBlur("firstname", e.target.value)}
                placeholder="First name"
              />
              {errorMessages.firstname && <p>{errorMessages.firstname}</p>}
            </div>
            <div className="text-input">
              <label htmlFor="firstname">Last name: </label>
              <input
                type="text"
                name="lastname"
                value={lastname}
                onChange={(e) => setLastname(e.target.value)}
                onBlur={(e) => handleOnBlur("lastname", e.target.value)}
                placeholder="Last name"
              />
              {errorMessages.lastname && <p>{errorMessages.lastname}</p>}
            </div>
            <div className="text-input">
              <label htmlFor="firstname">Password: </label>
              <input
                type="password"
                name="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                onBlur={(e) => handleOnBlurPassword(e.target.value)}
                placeholder="Password"
              />
              {errorMessages.password && <p>{errorMessages.password}</p>}
            </div>
            <button disabled={loading} type="submit">
              Submit
            </button>
          </form>
        </div>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search