简体   繁体   中英

how to prevent two same useState hooks from being triggered on one click event

I have 2 components with similar logic, they both conditionally display a message, based on the local state from useState hook and two props from the redux state. However, the problem occurs when I click on the ConnectedPasswordChange button - I see two messages, one from its own component and one from ProfileForm. Any idea how to get rid of that? Does it have something to do with event bubbling?

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 is used in ProfileForm component that is using the same logic

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>
  );
};

It's not typical to nest forms, so yes it appears if you submit an inner form, it also bubbles and triggers a submit event on the outer form component as well. To prevent this you can use 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>

Note that the resulting HTML is not valid, as <form> elements are not allowed to contain other <form> elements . If you try to statically render this HTML using a server-side React framework like Next.js you may find that the document may not end up as you expect:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM