如何防止一次单击事件触发两个相同的useState挂钩

时间:2020-05-14 15:15:03

标签: javascript reactjs react-hooks use-state

我有两个逻辑相似的组件,它们都基于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>
  );
};

1 个答案:

答案 0 :(得分:0)

嵌套表单并不常见,因此,是的,如果您提交内部表单,它会出现,它还会冒泡并触发外部表单组件上的Submit事件。为防止这种情况,您可以使用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>元素are not allowed to contain other <form> elements。如果您尝试使用Next.js之类的服务器端React框架来静态呈现此HTML,则可能会发现文档可能无法按预期结束:

// 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>