React-Hook-Form模态框确认后无法直接提交的问题求助
React Hook Form 提交确认模态框无法触发表单提交问题
问题场景
点击表单提交按钮后弹出确认模态框,用户点击确认后无法直接提交表单,必须关闭模态框后再次点击提交按钮才能完成提交。尝试将模态框放在FormSubmitButton组件及表单组件内部均未解决。
相关组件代码
FormSubmitButton组件
import { Svg } from "@/components/Svg"; import { Box, Button, ButtonProps } from "@chakra-ui/react"; import type { FieldValues, FormState } from "react-hook-form"; export type FormSubmitButtonProps<FV extends FieldValues> = ButtonProps & { formState: FormState<FV>; folder?: string; iconName?: string; wide?: boolean; }; const FormSubmitButton = <FV extends FieldValues>({ folder, iconName, name, formState, wide, ...props }: FormSubmitButtonProps<FV>) => { const { isSubmitting } = formState; return ( <Button type="submit" isLoading={isSubmitting} isDisabled={isSubmitting} {...props} > {!wide && folder && iconName && ( <Box mr="8px"> <Svg folder={folder} iconName={iconName} /> </Box> )} {name} {wide && folder && iconName && ( <Box ml="8px"> <Svg folder={folder} iconName={iconName} /> </Box> )} </Button> ); }; export default FormSubmitButton;
用户表单组件
import { FC, useState } from "react"; import { VStack, HStack, Stack } from "@chakra-ui/react"; import { IconButton } from "@/components/IconButton"; import { useFormContext } from "react-hook-form"; import { createInputList } from "@/components/form"; import { LeftFormSection } from "./LeftFormSection"; import { RightFormSection } from "./RightFormSection"; import { UserSchemaType } from "../NewUser"; import { useParams } from "react-router"; import { useAccountPasswordCreate } from "@/api/queries/account"; import { ConfirmationModal } from "@/components/ConfirmationModal"; const { FormSubmitButton } = createInputList<UserSchemaType>(); export const UserForm: FC = () => { const { formState, reset, getValues } = useFormContext<UserSchemaType>(); const { id: UserId } = useParams<{ id: string }>(); const { isDirty } = formState; const [showClearConfirmation, setShowClearConfirmation] = useState(false); const [showPasswordConfirmation, setShowPasswordConfirmation] = useState(false); const { mutate: passwordCreate } = useAccountPasswordCreate(); const email = getValues("email"); const handleNewPassword = () => { passwordCreate({ data: { email } }); }; const handleResetForm = () => { reset(); }; const handleClearConfirmation = () => { setShowClearConfirmation(true); }; const handleClearConfirmationCancel = () => { setShowClearConfirmation(false); }; const handleClearConfirmationConfirm = () => { handleResetForm(); }; const handlePasswordConfirmationConfirm = async () => { handleNewPassword(); setShowPasswordConfirmation(false); }; const handlePasswordConfirmation = () => { setShowPasswordConfirmation(true); }; const handlePasswordConfirmationCancel = () => { setShowPasswordConfirmation(false); }; return ( <VStack bg="white" borderRadius="24px" w={{ base: "full", md: "1130px" }} maxW={{ base: "full", md: "1130px" }} p="40px" > <Stack gap={4} alignItems="flex-start" w="full" flexDir={{ base: "column", md: "row" }} > <LeftFormSection /> <RightFormSection /> </Stack> <HStack data-testid="form-footer" w="full" justifyContent="flex-end" pt={4} > <IconButton name="Zrušit" folder="menu" iconName="close" variant="outline" colorScheme="darkGray" onClick={handleClearConfirmation} /> {UserId && ( <IconButton folder="menu" iconName="edit" colorScheme="darkGray" variant="outline" name="Nové heslo" onClick={handlePasswordConfirmation} /> )} <FormSubmitButton name="Uložit" iconName="ulozhit" folder="menu" formState={formState} isDisabled={!isDirty} /> </HStack> <ConfirmationModal isOpen={showPasswordConfirmation} onClose={handlePasswordConfirmationCancel} onConfirm={handlePasswordConfirmationConfirm} title="Jste si jisti, že chcete změnit heslo?" message="Tímto aktem změníte heslo uživatele. Jste si jisti?" confirmButtonText="Ano, změnit heslo" cancelButtonText="Zrušit" /> <ConfirmationModal isOpen={showClearConfirmation} onClose={handleClearConfirmationCancel} onConfirm={handleClearConfirmationConfirm} title="Jasné potvrzení formuláře" message="Jste si jisti, že chcete formulář vymazat?" confirmButtonText="Ano, čirá" cancelButtonText="Zrušit" /> </VStack> ); };
ConfirmationModal组件
import { Button, Modal, ModalBody, ModalContent, ModalFooter, ModalHeader, ModalOverlay, } from "@chakra-ui/react"; import { FC } from "react"; interface ConfirmationModalProps { isOpen: boolean; onClose: VoidFunction; onConfirm: VoidFunction; title: string; message: string; confirmButtonText: string; cancelButtonText: string; } export const ConfirmationModal: FC<ConfirmationModalProps> = ({ isOpen, onClose, onConfirm, title, message, confirmButtonText, cancelButtonText, }) => { const handleConfirm = () => { onConfirm(); onClose(); }; return ( <Modal isOpen={isOpen} onClose={onClose}> <ModalOverlay /> <ModalContent> <ModalHeader>{title}</ModalHeader> <ModalBody>{message}</ModalBody> <ModalFooter> <Button colorScheme="red" mr={3} onClick={handleConfirm}> {confirmButtonText} </Button> <Button variant="ghost" onClick={onClose}> {cancelButtonText} </Button> </ModalFooter> </ModalContent> </Modal> ); };
问题原因
提交按钮是原生type="submit"按钮,点击后直接触发表单默认提交流程,但弹出模态框中断了该流程;同时模态框确认时没有手动触发表单提交动作,导致第一次点击仅弹出模态框,未执行提交逻辑,第二次点击时模态框已关闭,才触发默认提交。
解决方案
1. 核心修改:拦截默认提交,手动触发提交逻辑
修改用户表单组件,添加提交确认模态框状态,拦截提交按钮的默认行为,在模态框确认后手动执行表单提交:
import { FC, useState } from "react"; import { VStack, HStack, Stack } from "@chakra-ui/react"; import { IconButton } from "@/components/IconButton"; import { useFormContext } from "react-hook-form"; import { createInputList } from "@/components/form"; import { LeftFormSection } from "./LeftFormSection"; import { RightFormSection } from "./RightFormSection"; import { UserSchemaType } from "../NewUser"; import { useParams } from "react-router"; import { useAccountPasswordCreate } from "@/api/queries/account"; import { ConfirmationModal } from "@/components/ConfirmationModal"; const { FormSubmitButton } = createInputList<UserSchemaType>(); export const UserForm: FC = () => { const { formState, reset, getValues, handleSubmit } = useFormContext<UserSchemaType>(); const { id: UserId } = useParams<{ id: string }>(); const { isDirty, isSubmitting } = formState; const [showClearConfirmation, setShowClearConfirmation] = useState(false); const [showPasswordConfirmation, setShowPasswordConfirmation] = useState(false); // 新增提交确认模态框状态 const [showSubmitConfirmation, setShowSubmitConfirmation] = useState(false); const { mutate: passwordCreate } = useAccountPasswordCreate(); // 替换为你的实际表单提交逻辑 const onSubmit = (data: UserSchemaType) => { console.log("表单提交数据:", data); // 这里添加API调用等提交逻辑 }; const email = getValues("email"); const handleNewPassword = () => { passwordCreate({ data: { email } }); }; const handleResetForm = () => { reset(); }; const handleClearConfirmation = () => { setShowClearConfirmation(true); }; const handleClearConfirmationCancel = () => { setShowClearConfirmation(false); }; const handleClearConfirmationConfirm = () => { handleResetForm(); setShowClearConfirmation(false); }; const handlePasswordConfirmationConfirm = async () => { handleNewPassword(); setShowPasswordConfirmation(false); }; const handlePasswordConfirmation = () => { setShowPasswordConfirmation(true); }; const handlePasswordConfirmationCancel = () => { setShowPasswordConfirmation(false); }; // 点击提交按钮:弹出确认模态框 const handleSubmitClick = () => { setShowSubmitConfirmation(true); }; // 确认提交:手动触发表单提交 const handleSubmitConfirm = () => { handleSubmit(onSubmit)(); setShowSubmitConfirmation(false); }; const handleSubmitCancel = () => { setShowSubmitConfirmation(false); }; return ( <VStack bg="white" borderRadius="24px" w={{ base: "full", md: "1130px" }} maxW={{ base: "full", md: "1130px" }} p="40px" > <Stack gap={4} alignItems="flex-start" w="full" flexDir={{ base: "column", md: "row" }} > <LeftFormSection /> <RightFormSection /> </Stack> <HStack data-testid="form-footer" w="full" justifyContent="flex-end" pt={4} > <IconButton name="Zrušit" folder="menu" iconName="close" variant="outline" colorScheme="darkGray" onClick={handleClearConfirmation} /> {UserId && ( <IconButton folder="menu" iconName="edit" colorScheme="darkGray" variant="outline" name="Nové heslo" onClick={handlePasswordConfirmation} /> )} {/* 修改提交按钮:改为button类型,绑定自定义点击事件 */} <FormSubmitButton name="Uložit" iconName="ulozhit" folder="menu" formState={formState} isDisabled={!isDirty || isSubmitting} onClick={handleSubmitClick} type="button" /> </HStack> {/* 新增提交确认模态框 */} <ConfirmationModal isOpen={showSubmitConfirmation} onClose={handleSubmitCancel} onConfirm={handleSubmitConfirm} title="Potvrzení odeslání formuláře" message="Jste si jisti, že chcete uložit změny?" confirmButtonText="Ano, uložit" cancelButtonText="Zrušit" /> <ConfirmationModal isOpen={showPasswordConfirmation} onClose={handlePasswordConfirmationCancel} onConfirm={handlePasswordConfirmationConfirm} title="Jste si jisti, že chcete změnit heslo?" message="Tímto aktem změníte heslo uživatele. Jste si jisti?" confirmButtonText="Ano, změnit heslo" cancelButtonText="Zrušit" /> <ConfirmationModal isOpen={showClearConfirmation} onClose={handleClearConfirmationCancel} onConfirm={handleClearConfirmationConfirm} title="Jasné potvrzení formuláře" message="Jste si jisti, že chcete formulář vymazat?" confirmButtonText="Ano, čirá" cancelButtonText="Zrušit" /> </VStack> ); };
2. 可选优化:自动切换按钮类型
修改FormSubmitButton组件,当传递自定义onClick事件时自动切换为button类型,无需手动指定:
import { Svg } from "@/components/Svg"; import { Box, Button, ButtonProps } from "@chakra-ui/react"; import type { FieldValues, FormState } from "react-hook-form"; export type FormSubmitButtonProps<FV extends FieldValues> = ButtonProps & { formState: FormState<FV>; folder?: string; iconName?: string; wide?: boolean; }; const FormSubmitButton = <FV extends FieldValues>({ folder, iconName, name, formState, wide, onClick, ...props }: FormSubmitButtonProps<FV>) => { const { isSubmitting } = formState; return ( <Button type={onClick ? "button" : "submit"} isLoading={isSubmitting} isDisabled={isSubmitting} onClick={onClick} {...props} > {!wide && folder && iconName && ( <Box mr="8px"> <Svg folder={folder} iconName={iconName} /> </Box> )} {name} {wide && folder && iconName && ( <Box ml="8px"> <Svg folder={folder} iconName={iconName} /> </Box> )} </Button> ); }; export default FormSubmitButton;
内容的提问来源于stack exchange,提问作者krzhkv
相关产品推荐
相关产品推荐

