React 表单 useState 对象无法按我的意愿工作

Posted

技术标签:

【中文标题】React 表单 useState 对象无法按我的意愿工作【英文标题】:React form useState object doesn't work as I want 【发布时间】:2022-01-24 00:44:00 【问题描述】:

我的 React 应用程序有问题。我有一个带有两个输入的表单,当我提交带有空输入的表单时,它应该在每个输入中呈现一条错误消息。问题是它没有显示第一个输入。如何修复它以显示每个错误?实现在useForm.js

FormUI 图片:

我的代码:

Form.js

const Form = () => 
  
const formLogin = () => 
  console.log("Callback function when form is submitted!");
  console.log("Form Values ", values);


const handleChange, values, errors, handleSubmit = useForm(formLogin);


  return (
    <Wrapper>
      <form onSubmit=handleSubmit>

        <div className="govgr-form-group gap-bottom">
          <label className="govgr-label govgr-!-font-weight-bold" htmlFor="code">Code*</label>
          errors.code && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>errors.code</p>
          <input className=`govgr-input govgr-!-width-three-quarter $errors.code ? 'govgr-error-input' : ''` id="code" name="code" type="text" onChange=handleChange />
        </div>

        <fieldset>
          <div className="govgr-form-group">
            <label className="govgr-label govgr-!-font-weight-bold" htmlFor="first">Name*</label>
            errors.first && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>errors.first</p>
            <input className=`govgr-input govgr-!-width-three-quarter $errors.first ? 'govgr-error-input' : ''` id="first" name="first" type="text" onChange=handleChange />
          </div>

         
        </fieldset>

        <button type="submit" className="govgr-btn govgr-btn-primary btn-center">Save</button>

      </form>
    </Wrapper>
  );
;

export default Form;

useForm.js:

const useForm = (callback) => 
  
  const [values, setValues] = useState();
  
  const [errors, setErrors] = useState();

  const validate = (event, name, value) => 
    
    event.persist();

    switch (name) 
      case "code":
        if (value.trim() === "" || value.trim() === null) 
          setErrors(
            ...errors,
            code: "Code is required",
          );
         else 
          let newObj = omit(errors, "code");
          setErrors(newObj);
        
        break;

      case "first":
        if (value.trim() === "" || value.trim() === null) 
          setErrors(
            ...errors,
            first: "Name is required",
          );
         else 
          let newObj = omit(errors, "first");
          setErrors(newObj);
        
        break;

        

      default:
        break;
    
  ;

  
  const handleChange = (event) => 
    event.persist();

    let name = event.target.name;
    let val = event.target.value;

    validate(event, name, val);

    setValues(
      ...values,
      [name]: val,
    );
  ;

  const handleSubmit = (event) => 
    if (event) event.preventDefault();

    if (
      Object.keys(errors).length === 0 &&
      Object.keys(values).length !== 0 &&
      values.code &&
      values.first 
    ) 
      callback();
     else 
      if (!values.code) 
        setErrors(
          ...errors,
          code: "Code is required.",
        );
      
      if (!values.first) 
        setErrors(
          ...errors,
          first: "Name is required.",
        );
      
    
  ;

  return 
    values,
    errors,
    handleChange,
    handleSubmit
  ;
;

export default useForm;

【问题讨论】:

此代码中不需要event.persist()。这仅适用于异步处理程序。此外,您复制了验证码。这些都不会导致您的错误,但无论如何您都应该注意它们。 您不应该在setErrors 调用中使用errors。您应该改用此构造:setErrors(e =&gt; (...e, code: "Code is required"))。尝试删除错误时同样适用:使用setErrors(e =&gt; e.filter(e.hasOwnProperty('code')),或类似的东西。 我不认为你可以在这里使用 switch 语句,默认情况下 switch 语句返回许多选项之一。因此,您只会返回一个错误,您可以在此处阅读更多信息 -> w3schools.com/js/js_switch.asp 我也认为这段代码对于您想要实现的目标来说过于复杂,也许需要重新编写。 @snazzyy - validate/switch 是从 onchange 事件中调用的,它只在单个字段中被调用,所以它是正确的。我同意这段代码太复杂了。 你是对的@SoftwareEngineer 【参考方案1】:

您可以通过只有一个验证例程来简化您的代码。您可以通过使用框架传递给setState 的当前状态来修复您提到的错误,一次只有一个错误。这个结构是

setState(e => /* whatever you want to return relative to e */);

最终,您看到的错误是因为组件及其代码在每次状态更改后都会重新呈现(但出于性能原因它们是批处理的),因此您的代码仅在以下情况下看到旧版本的状态你直接访问它。如果您在更改时想要实际的当前状态,最好让框架直接将其传递给 change-state 函数。如果您始终遵循这种模式,那么您很少会遇到 react 状态模型的任何问题。

然而,难题不在于这些。难题是您消除错误的方式。首先,从 UI 的角度来看,仅在编辑字段时将其显示为错误有点不一致,因为表单在首次显示时是错误的。但是,忽略这一点,潜在的技术问题是从对象中删除属性。通常,当我们需要这样做时,我们会使用数组而不是对象,或者我们会使用下划线的omit 函数。我在您的代码中看到您调用了一个名为omit 的函数,但您没有解释它是什么。不过,在您的情况下,这可以通过从不删除该属性来轻松解决。相反,将每个作为具有有效标志的对象。这将允许您完全删除 switch 语句。

你也可以更进一步,在那里滚动值,为每个字段创建一个完整的单一状态,但我不会在这里演示。

我还没有测试过这个,所以这里可能有一两个错字。

const useForm = (callback) => 
  const [values, setValues] = useState(code: '', first: '');
  const [errors, setErrors] = useState(first: message: "Name is required", valid: true, code: message: "Code is required", valid: true);

  const isEmpty = val => val.trim() === "" || val.trim() === null);

  const validate = (name, val) => 
    setErrors(e => (...e, [name]: ...e[name], valid: isEmpty(val)))
    return errors[name].valid;
  ;
  
  const handleChange = (event) => 
    let name = event.target.name;
    let val = event.target.value;
    setValues(...values, [name]: val);
    validate(name, val);
  ;

  const handleSubmit = event => 
    if (event) event.preventDefault();

    const valid = validate('code', values.code) && validate('first', values.first);

    if ( valid ) 
      callback();
     
  ;

  return 
    values,
    errors,
    handleChange,
    handleSubmit
  ;
;

export default useForm;

而且,您必须稍微更改您的 HTML 模板:

!errors.first.valid && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>errors.first.message</p>

我意识到这个新的validate 函数与您拥有的功能大相径庭,但根据您自己的代码,我认为您可以处理它。基本上,它只是使用解构和变量属性访问器。这看起来在您的理解能力范围内,但如果其中任何一个令人困惑或实际上不起作用,请随时提出更多问题:)

【讨论】:

以上是关于React 表单 useState 对象无法按我的意愿工作的主要内容,如果未能解决你的问题,请参考以下文章

无法更新状态 - react-native

使用 Axios 将对象从 React.js 发布到 PHP

React.js useState hook 导致过多的重新渲染并且无法更新我的状态

我的 React useState 不能立即在 useEffect 中设置对象。有人能帮我吗?

React Hooks:使用useState更新状态不会立即更新状态[重复]

react usestate 重新设置数据后,不渲染页面