Next.js 14 + NextUI:表单提交成功后关闭模态框求助
解决Next.js 14 + NextUI模态框表单提交后关闭问题
核心思路
服务端Action无法直接调用客户端的模态框关闭方法,我们可以利用useFormState返回的状态变化,在客户端监听提交成功的信号,触发模态框关闭。
具体修改步骤
1. 修复模态框内部onClose命名冲突
你的代码中ModalContent回调的onClose参数与组件自身useDisclosure的onClose重名,会导致逻辑混淆,先修改内部命名:
<ModalContent> {/* 把参数名改成handleModalClose,避免和组件的onClose冲突 */} {(handleModalClose) => ( <> <form action={formAction}> {/* ... 其他表单内容 ... */} <ModalFooter> {state?.message && <Code>{state.message}</Code>} <Button color="danger" variant="light" onPress={handleModalClose}> Close </Button> <SubmitButton /> </ModalFooter> </form> </> )} </ModalContent>
2. 监听提交成功状态,触发模态框关闭
导入useEffect,监听useFormState返回的state,当状态为成功的候选对象(无错误信息)时,调用组件的onClose方法:
// 先导入useEffect import React, { useEffect } from "react"; // 在组件内部添加监听逻辑 export default function AddCandidateModal({ campaign_id, status }: { campaign_id: number, status: CandidateStatusType[] }) { const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure(); const { register } = useForm() const [state, formAction] = useFormState(createCandidateAction, null) // 监听state变化,提交成功则关闭模态框 useEffect(() => { // 当state存在且没有错误message时,判定为提交成功 if (state && !state.message) { onClose(); } }, [state, onClose]); // ... 其余代码不变 }
3. 可选:保留redirect逻辑的处理
如果需要在提交成功后跳转页面,redirect会触发页面刷新,此时模态框的isOpen状态会自动重置为初始的false,但这种方式会刷新页面,体验不如无刷新关闭流畅,推荐优先使用第一种方案。
修改后的完整AddCandidateModal.tsx
import React, { useEffect } from "react"; import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button, useDisclosure, Input, Code, Select, SelectItem } from "@nextui-org/react"; import { PlusIcon } from "./PlusIcon"; import { useFormState, useFormStatus } from "react-dom"; import { useForm } from "react-hook-form"; import { createCandidateAction } from "@/app/actions/candidate-action"; import { CandidateStatusType } from "@/types/campaign"; function SubmitButton() { const { pending } = useFormStatus() return ( <Button color="primary" aria-disabled={pending} type="submit" isLoading={pending}>Save</Button> ) } export default function AddCandidateModal({ campaign_id, status }: { campaign_id: number, status: CandidateStatusType[] }) { const { isOpen, onOpen, onOpenChange, onClose } = useDisclosure(); const { register } = useForm() const [state, formAction] = useFormState(createCandidateAction, null) useEffect(() => { if (state && !state.message) { onClose(); } }, [state, onClose]); return ( <> <Button onPress={onOpen} color="primary" endContent={<PlusIcon />}>Add New</Button> <Modal isOpen={isOpen} onOpenChange={onOpenChange}> <ModalContent> {(handleModalClose) => ( <> <form action={formAction} > <ModalHeader className="flex flex-col gap-1">New Candidate</ModalHeader> <ModalBody> <Select items={status} label="Candidate Status" placeholder="Select a status" className="" isRequired {...register('status')} > {(item) => <SelectItem key={item.id}>{item.name}</SelectItem>} </Select> <div className="grid grid-cols-2 gap-3"> <Input isRequired label="First Name" placeholder="Travis" {...register('first_name')} /> <Input isRequired label="Last Name" placeholder="Scott" {...register('last_name')} /> </div> <Input isRequired type="email" label="Email" placeholder="scott@tra.org" {...register('email')} /> <Input isRequired type="phone" label="Phone" placeholder="123 45678" {...register('phone_number')} /> <span className="text-gray-500">Upload Resume</span> <input className=" w-full py-3 px-3 text-gray-500 rounded-lg cursor-pointer bg-gray-100 " type="file" /> <input type="hidden" value={campaign_id} {...register("campaign")} /> </ModalBody> <ModalFooter> {state?.message && <Code>{state.message}</Code>} <Button color="danger" variant="light" onPress={handleModalClose}> Close </Button> <SubmitButton /> </ModalFooter> </form> </> )} </ModalContent> </Modal> </> ); }
内容的提问来源于stack exchange,提问作者Maruf Khan
相关产品推荐
相关产品推荐

