如何实现shadcn Dialog内嵌表单仅成功提交时关闭?
解决Dialog内嵌表单仅提交成功时关闭弹窗的问题
问题背景
当前在shadcn的Dialog组件内嵌了基于react-hook-form与zod构建的表单,将提交按钮包裹在DialogClose组件中后,无论表单提交成功与否弹窗都会关闭。需求是仅在表单提交成功时关闭Dialog,询问是否可将DialogClose作为函数使用及实现方式。
解决方案
Radix UI的DialogClose是组件而非函数,无法直接通过函数调用触发关闭。但可以通过Dialog的状态控制方法手动实现仅成功时关闭弹窗,具体步骤如下:
- 传递Dialog的状态控制方法:修改
PecuniaForm的Props,添加用于控制Dialog显示/隐藏的setOpen方法(该方法来自Dialog组件的状态或useDialog钩子)。 - 移除DialogClose包裹:将提交按钮外的
DialogClose组件移除,改为直接渲染Button。 - 成功时手动关闭Dialog:在表单提交成功的逻辑分支中,调用
setOpen(false)关闭弹窗。
修改后的完整表单代码
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/form' import { Input } from '@/components/ui/input' import { useForm } from 'react-hook-form' import { pecuniaFormSchema } from './PecuniaFormSchema' import { z } from 'zod' import { zodResolver } from '@hookform/resolvers/zod' import { Button } from '@/components/ui/button' import { updateFinancialRequest } from '@/app/services/financialService' import { ErrorMapping } from '@/utils/errorMapping' import { useToast } from '@/components/ui/use-toast' import { objectToFormData } from '@/utils/objectToFormData' // 新增Props:接收Dialog的状态控制方法 interface PecuniaFormProps { pecuniaId: number setOpen: (open: boolean) => void } export default function PecuniaForm({ pecuniaId, setOpen }: PecuniaFormProps) { const form = useForm<z.infer<typeof pecuniaFormSchema>>({ resolver: zodResolver(pecuniaFormSchema), }) const { toast } = useToast() async function handleSubmitData(values: z.infer<typeof pecuniaFormSchema>) { let erroTreatment try { const response = await updateFinancialRequest( objectToFormData({ ...values, id: pecuniaId, }), ) if (response !== 'SUCCESS_UPDATE') { erroTreatment = ErrorMapping(response.technicalMessage) throw new Error(`Response status: ${response.status}`) } toast({ title: 'Dados financeiro atualizados com sucesso', duration: 3000, }) // 提交成功后手动关闭Dialog setOpen(false) } catch (err) { toast({ variant: 'destructive', title: erroTreatment || 'Erro ao realizar solicitação', duration: 4000, }) } } return ( <Form {...form}> <form onSubmit={form.handleSubmit(handleSubmitData)} className="space-y-8" > <FormField control={form.control} name="numeroEmpenho" render={({ field }) => ( <FormItem> <FormLabel>Número de Empenho</FormLabel> <FormControl> <Input placeholder="Digite o valor de empenho" type="number" {...field} onChange={(e) => field.onChange( e.target.value === '' ? undefined : Number(e.target.value), ) } /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="ob" render={({ field }) => ( <FormItem> <FormLabel>Ordem Bancária</FormLabel> <FormControl> <Input placeholder="Digite o valor de OB" type="number" {...field} onChange={(e) => field.onChange( e.target.value === '' ? undefined : Number(e.target.value), ) } /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="liquidacao" render={({ field }) => ( <FormItem> <FormLabel>Número de Liquidação</FormLabel> <FormControl> <Input placeholder="Digite o valor de Liquidação" type="number" {...field} onChange={(e) => field.onChange( e.target.value === '' ? undefined : Number(e.target.value), ) } /> </FormControl> <FormMessage /> </FormItem> )} /> <FormField control={form.control} name="anexoFinanceiro" render={({ field }) => ( <FormItem> <FormLabel>Anexo Financeiro</FormLabel> <FormControl> <Input type="file" onChange={(e) => { const file = e.target.files?.[0] field.onChange(file) }} /> </FormControl> <FormMessage /> </FormItem> )} /> {/* 移除DialogClose包裹,直接渲染提交按钮 */} <Button type="submit">Confirmar Solicitação</Button> </form> </Form> ) }
父组件中传递状态控制方法示例
import { useState } from 'react' import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog' import PecuniaForm from './PecuniaForm' import { Button } from '@/components/ui/button' export default function ParentComponent() { const [open, setOpen] = useState(false) const pecuniaId = 123 // 示例ID return ( <Dialog open={open} onOpenChange={setOpen}> <DialogTrigger asChild> <Button>Abrir Formulário</Button> </DialogTrigger> <DialogContent> {/* 将setOpen传递给表单组件 */} <PecuniaForm pecuniaId={pecuniaId} setOpen={setOpen} /> </DialogContent> </Dialog> ) }
内容的提问来源于stack exchange,提问作者Mittzera
相关产品推荐
相关产品推荐

