как предотвратить срабатывание двух одинаковых хуков useState при одном событии щелчка

У меня есть 2 компонента с аналогичной логикой, они оба условно отображают сообщение на основе локального состояния из хука useState и двух реквизитов из состояния redux. Однако проблема возникает, когда я нажимаю кнопку ConnectedPasswordChange - я вижу два сообщения, одно от его собственного компонента и одно от ProfileForm. Есть идеи, как от этого избавиться? Это как-то связано с всплыванием событий?

const ConnectedPasswordChange = (): Node => {
  const [submitted, setSubmitted] = useState(false);

  const isUpdating = useSelector(state => state.user.isUpdating);
  const updateError = useSelector(state => state.user.updateError);

  const handleSubmit = () => {
    const { currentPassword, newPassword } = passwordDetails;
    dispatch(passwordUpdateRequested(currentPassword, newPassword));
    setSubmitted(true);
    setTimeout(() => setSubmitted(false), 5000);
  };

  return (
    <Form onSubmit={handleSubmit}>
      <PasswordChange passwordDetails={passwordDetails} onPasswordChange={setPasswordDetails} />
      <div style={{ textAlign: "center" }}>
        <Form.Button secondary>Button</Form.Button>
      </div>
      {submitted && !isUpdating && !updateError && <Message positive content="Success" />}
    </Form>
  );
};

ConnectedPasswordChange используется в компоненте ProfileForm, использующем ту же логику.

const ProfileForm = ({ id, user: initialUser, locations, isUpdating, updateError, onSubmit }: Props): Node => {

  const [submitted, setSubmitted] = useState(false);

  const handleSubmit = () => {
    onSubmit(id);
    setSubmitted(true);
    setTimeout(() => setSubmitted(false), 5000);
  };

  return (
    <Segment className="my-profile-main-container">
      <Title />
      <Divider />
      <Form onSubmit={handleSubmit}>
        <PersonalDetails personalDetails={user} onPersonalDetailsChange={handleDetailsChange} />
        <Divider />
        <BankDetails bankDetails={user} onBankDetailsChange={handleDetailsChange} />
        <div style={{ textAlign: "center" }}>
          <Form.Button secondary>Button</Form.Button>
        </div>
        {submitted && !isUpdating && !updateError && <Message positive content={t("updateSucceeded")} />}
        <Divider />
        <ConnectedPasswordChange />
      </Form>
    </Segment>
  );
};

person Dominika Bieder    schedule 14.05.2020    source источник
comment
Попробуйте использовать e.stopPropagation () в своей функции handleSubmit внутри ConnectedPasswordChange   -  person LuisMendes535    schedule 14.05.2020


Ответы (1)


Это не типично для вложенных форм, поэтому да, если вы отправляете внутреннюю форму, она также всплывает и запускает событие отправки и во внешнем компоненте формы. Чтобы предотвратить это, вы можете использовать Event.prototype.stopPropagation():

const ProfileForm = () => {
  const tableRef = React.useRef();
  const handleSubmit = e => {
    e.preventDefault();
    tableRef.current.insertRow().append('ProfileForm submitted');
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <fieldset>
        <legend>ProfileForm</legend>
        <ConnectedPasswordChange />
        <input type="submit" />
        <table ref={tableRef} />
      </fieldset>
    </form>
  );
};

const ConnectedPasswordChange = () => {
  const tableRef = React.useRef();
  const handleSubmit = e => {
    e.preventDefault();
    e.stopPropagation(); // important! without this, ProfileForm will submit as well
    tableRef.current.insertRow().append('ConnectedPasswordChange submitted');
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <fieldset>
        <legend>ConnectedPasswordChange</legend>
        <input type="submit" />
        <table ref={tableRef} />
      </fieldset>
    </form>
  );
};

ReactDOM.render(<ProfileForm />, document.querySelector('main'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>

<main></main>

Обратите внимание, что полученный HTML-код недействителен, поскольку <form> элементы не являются разрешено содержать другие <form> элементы. Если вы попытаетесь статически отобразить этот HTML-код с помощью серверной инфраструктуры React, такой как Next.js, вы можете обнаружить, что документ может закончиться не так, как вы ожидаете:

// the inner <form> was stripped from the markup
console.log(document.getElementById('ConnectedPasswordChange'));
console.log(document.querySelector('main').outerHTML);
<main>
  <form id="ProfileForm">
    <fieldset>
      <legend>ProfileForm</legend>
      <form id="ConnectedPasswordChange">
        <fieldset>
          <legend>ConnectedPasswordChange</legend>
          <input type="submit" />
          <table></table>
        </fieldset>
      </form>
      <input type="submit" />
      <table></table>
    </fieldset>
  </form>
</main>

person Patrick Roberts    schedule 14.05.2020