why doesn’t validation of input on eventlistener work?

There are a few problems, but they all come from the same root – state updates to not take place immediately.

Problem one is that you cannot use the updated state right after calling setState. This is causing your validation logic to execute using the form values from the previous render.

Problem two is that if you call a state updater function twice in a row, it will only use the last value. So in your validation logic, you call setFormError once based on the value of login, and once based on the value of password. This means the value determined by the login logic will never be used.

I’ve written an example of a few ways you can write this correctly.

Example one as a function called on each input change:

const { useState } = React;

const MyInput = (props) => {
  return (
    <input {...props} style={{ border: "1px solid red", outline: "none" }} />
  );
};

const Example = () => {
  const [form, setForm] = useState({
    login: "",
    password: ""
  });
  const [formError, setFormError] = useState({
    isValidLogin: "FAIL",
    isValidPassword: "FAIL"
  });

  const handleValidateForm = (newForm) => {
    let newValid = {...formError}; // Use a new object so we only update state once
    
    if (newForm.login.length > 3) {
      newValid.isValidLogin = "GOOD"
    } else {
      newValid.isValidLogin = "FAIL"
    }
    if (newForm.password.length > 3) {
      newValid.isValidPassword = "GOOD"
    } else {
      newValid.isValidPassword = "FAIL"
    }
    
    setFormError(newValid);
  };

  const onChange = (e) => {
    e.preventDefault();
    const { name, value } = e.target;
    
    const newForm = {...form, [name]: value}; // Set to new object

    setForm(newForm);
    
    handleValidateForm(newForm); // Send the new object so its up-to-date
  };

  return (
    <div>
      <div>
        <MyInput type="text" name="login" onChange={onChange} />{" "}
        {formError.isValidLogin}
        <br />
        <MyInput type="text" name="password" onChange={onChange} />{" "}
        {formError.isValidPassword}
      </div>
    </div>
  );
};


ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

The other approach would be to move the validation into a useEffect:

const { useState, useEffect } = React;

const MyInput = (props) => {
  return (
    <input {...props} style={{ border: "1px solid red", outline: "none" }} />
  );
};

const Example = () => {
  const [form, setForm] = useState({
    login: "",
    password: ""
  });
  const [formError, setFormError] = useState({
    isValidLogin: "FAIL",
    isValidPassword: "FAIL"
  });

  useEffect(() => {
    let newValid = {...formError}; // Use a new object so we only update state once
    
    if (form.login.length > 3) {
      newValid.isValidLogin = "GOOD"
    } else {
      newValid.isValidLogin = "FAIL"
    }
    if (form.password.length > 3) {
      newValid.isValidPassword = "GOOD"
    } else {
      newValid.isValidPassword = "FAIL"
    }
    
    setFormError(newValid);
  }, [form]); // Runs every time the form changes

  const onChange = (e) => {
    e.preventDefault();
    const { name, value } = e.target;
    
    setForm({...form, [name]: value});
  };

  return (
    <div>
      <div>
        <MyInput type="text" name="login" onChange={onChange} />{" "}
        {formError.isValidLogin}
        <br />
        <MyInput type="text" name="password" onChange={onChange} />{" "}
        {formError.isValidPassword}
      </div>
    </div>
  );
};


ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

It has the same effect, so its up to you which pattern you prefer.

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top