如何防止在单击事件上触发两个相同的 useState 挂钩
Posted
技术标签:
【中文标题】如何防止在单击事件上触发两个相同的 useState 挂钩【英文标题】:how to prevent two same useState hooks from being triggered on one click event 【发布时间】:2020-08-31 04:47:48 【问题描述】:我有 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>
);
;
在使用相同逻辑的 ProfileForm 组件中使用 ConnectedPasswordChange
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>
);
;
【问题讨论】:
尝试在 ConnectedPasswordChange 内的 handleSubmit 函数上使用 e.stopPropagation() 【参考方案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>
元素 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>
【讨论】:
以上是关于如何防止在单击事件上触发两个相同的 useState 挂钩的主要内容,如果未能解决你的问题,请参考以下文章