为什么不验证 eventListener 上的输入?

why doesn't validating input on eventListener work?

你能帮我让这个沙盒工作吗? 我想念一些东西。

当密码或登录名的长度超过 3 时,我希望在输入附近看到“GOOD”而不是“FAIL”。

这里是 link:https://codesandbox.io/s/competent-bogdan-5604u?file=/src/App.js

谢谢!

有一些问题,但它们都来自同一个根源 - 状态更新不会立即发生。

问题一是您无法在调用 setState 后立即使用更新后的状态。这导致您的验证逻辑使用 previous 渲染中的表单值执行。

问题二是如果你连续两次调用状态更新函数,它只会使用最后一个值。因此,在您的验证逻辑中,您根据 login 的值调用一次 setFormError,并根据 password 的值调用一次。这意味着永远不会使用由 login 逻辑确定的值。

我已经写了一个例子,你可以用几种方法正确地写这个。

示例一作为每次输入更改时调用的函数:

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>

另一种方法是将验证移至 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>

效果一样,就看你喜欢哪种模式了。