React 表单如何根据切换获取用户数据 on/off

React form how to get user data based on a toggle on/off

我在 React JS 中有一个表单 toggle/switch。如果 toggle/switch 打开,则屏幕上会出现两个输入。因此,如果用户输入输入并且 toggle/switch 打开并保持打开状态,我想获取用户数据。因此,如果用户输入输入但他再次 toggles/switches 关闭,则输入​​值将被重置,当他保存表单时,我必须得到空的用户数据(我得到初始值)。我怎样才能实现这样的目标?如果切换按钮为假,我正在检查提交处理程序并将使用状态设置为初始值,但它不起作用。

我的代码:

Form.js

import React, { useRef, useState } from "react";
import Wrapper from "./UI/Wrapper";
import Switch from '@mui/material/Switch';
import "./Form.css";

const Form = () => {

  const [showCertification, setShowCertification] = useState(false);
  
  const [enteredCodecert, setEnteredCodecert] = useState('');
  const codecertRef = useRef();

  const [codesteps, setCodesteps] = useState([{ value: null }]);
  const codestepsRef = useRef();

  const enteredCodecertIsValid = showCertification && enteredCodecert.trim() !== '';
  const codecertInputIsInvalid = !enteredCodecertIsValid;

  const codestepsIsValid = showCertification && codesteps.length >= 1 && codesteps.every(codestep => codestep.value !== null && codestep.value.trim() !== '');
  const codestepInputIsInvalid = !codestepsIsValid;


  const showCertificationHandler = (event) => {
    setShowCertification(prevState => !prevState);
    if (!showCertification) {
      setEnteredCodecert('');
      setCodesteps([{value: null}]);
    }
  }

  const codecertChangeHandler = (event) => {
    setEnteredCodecert(event.target.value);
  }

  const stepChangeHandler = (i, event) => {
    const values = [...codesteps];
    values[i].value = event.target.value;
    setCodesteps(values);
  }

  const addStepHandler = (event) => {
    event.preventDefault();
    const values = [...codesteps];
    values.push({ value: null });
    setCodesteps(values);
  }

  const removeStepHandler = (i, event) => {
    event.preventDefault();
    const values = [...codesteps];
    values.splice(i, 1);
    setCodesteps(values);
  }

  const submitHandler = (event) => {
    event.preventDefault();

    if (!enteredCodecertIsValid && showCertification) {
      codecertRef.current.focus();
      return;
    }

    if (!codestepsIsValid && showCertification) {
      if (codesteps.length >= 1) {
        codestepsRef.current.focus();
        return;
      }
      return;
    }

    if (showCertification === false) {
      setEnteredCodecert('');
      setCodesteps([{value: null}]);
    }

    console.log(enteredCodecert);
    console.log(codesteps);
  }


  return (
    <Wrapper>
      <form onSubmit={submitHandler}>

        <fieldset className={`${(showCertification && codecertInputIsInvalid) || (showCertification && codestepInputIsInvalid)  ? 'govgr-form-group__error' : '' }`}>
          <legend><h3 className="govgr-heading-m">Certifications</h3></legend>

          <Switch id="certification" checked={showCertification} onClick={showCertificationHandler} inputProps={{ 'aria-label': 'controlled' }} />
          <label className="govgr-label govgr-!-font-weight-bold cert-label" htmlFor="certification">Certification</label>
          {showCertification && (
          <div>
            <div className="govgr-form-group">
              <label className="govgr-label govgr-!-font-weight-bold" htmlFor="codecert">Code Certification*</label>
              {codecertInputIsInvalid && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>Code Certification is required.</p>}
              <input className={`govgr-input govgr-!-width-three-quarter ${codecertInputIsInvalid ? 'govgr-error-input' : ''}`} id="codecert" name="codecert" type="text" value={enteredCodecert} ref={codecertRef} onChange={codecertChangeHandler} />
           </div>

            <div className="govgr-form-group">
              <label className="govgr-label govgr-!-font-weight-bold" htmlFor="codestep">Code STEPS*</label>
              {codestepInputIsInvalid && <p className="govgr-error-message"><span className="govgr-visually-hidden">Λάθος:</span>Code STEPS are required.</p>}
                  {codesteps.map((field, idx) => {
                    return (
                    <div key={`${field}-${idx}`}>
                      <div className="flex-row">
                        <input className={`govgr-input govgr-input--width-10 input-step ${codestepInputIsInvalid ? 'govgr-error-input' : ''}`} id="codestep" type="text" ref={codestepsRef} value={field.value || ""} onChange={e => stepChangeHandler(idx, e)} />
                        <button className="govgr-btn govgr-btn-warning remove-step" onClick={(e) => removeStepHandler(idx, e)}>Χ</button>
                      </div>  
                    </div>
                    );
                  })}
                  <button className="govgr-btn govgr-btn-secondary button-step" onClick={addStepHandler}>Add Code Step</button>       
            </div>
          </div>
          )}
        </fieldset>

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

      </form>
    </Wrapper>
  );
};

export default Form;

问题是,在 showCertificationHandler 中,当您切换 showCertification 时,您期望状态更新是立即的。

const showCertificationHandler = (event) => {
  setShowCertification(prevState => !prevState);
  if (!showCertification) {
    setEnteredCodecert('');
    setCodesteps([{value: null}]);
  }
}

然而,React 状态更新不是这种情况。 React 状态更新已排队并异步处理。

要解决,请将“重置”逻辑移动到 useEffect 挂钩中,该挂钩依赖于 showCertification 状态。

const showCertificationHandler = () => {
  setShowCertification((prevState) => !prevState);
};

useEffect(() => {
  if (!showCertification) {
    setEnteredCodecert("");
    setCodesteps([{ value: null }]);
  }
}, [showCertification]);

出于与上述相同的原因,当重置 submitHandler 中的状态时,它们会被排队并异步处理,因此控制台在之后立即记录状态只会记录当前渲染周期的状态值,而不是它们将在随后的渲染周期中出现。您可以从 submitHandler.

中删除“重置”逻辑
const submitHandler = (event) => {
  event.preventDefault();

  if (!enteredCodecertIsValid && showCertification) {
    codecertRef.current.focus();
    return;
  }

  if (!codestepsIsValid && showCertification) {
    if (codesteps.length >= 1) {
      codestepsRef.current.focus();
      return;
    }
    return;
  }

  console.log({enteredCodecert, codesteps});
};