我有 2 个逻辑相似的组件,它们都根据 useState 钩子的本地状态和 redux 状态的两个 prop 有条件地显示一条消息。但是,当我单击"连接密码更改"按钮时,会出现此问题 - 我看到两条消息,一条来自其自己的组件,另一条来自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>
);
};
嵌套表单并不常见,所以是的,如果您提交内部表单,它就会出现,它还会在外部表单组件上冒泡并触发提交事件。为了防止这种情况,您可以使用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>
元素。如果您尝试使用像 Next 这样的服务器端 React 框架静态渲染此 HTML.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>