Next.js用户模态框中Shadcn日历组件的日期选择问题
问题修复方案
一、解决日历点击任意位置就关闭的问题
点击日历内部(包括年月选择器)导致Popover关闭,是因为事件冒泡触发了Popover的默认关闭逻辑。在PopoverContent上添加事件阻止冒泡:
<PopoverContent className="w-auto p-0" align="start" onClick={(e) => e.stopPropagation()}> {/* Calendar组件 */} </PopoverContent>
这样点击日历内部的任何元素都不会触发Popover关闭,就能正常选择年月了。
二、解决选日期后表单字段不更新的问题
- 确保Calendar的selected值类型正确
如果表单存储的是日期字符串(如ISO格式),需要把field.value转换为Date对象再传给Calendar:
selected={field.value ? (typeof field.value === 'string' ? new Date(field.value) : field.value) : undefined}
- 修正onSelect回调逻辑
点击年月选择器时,Calendar可能会返回undefined,直接调用field.onChange会清空字段。只在date有效时更新表单:
onSelect={(date) => { if (date) { // 如果表单需要存储Date对象,直接传date field.onChange(date); // 如果表单需要存储字符串,转成ISO格式: // field.onChange(date.toISOString()); } }}
- 初始化表单值时转换类型
打开模态框重置表单时,把用户数据里的日期字段转成对应类型(Date或字符串):
const handleOpenModal = (user: UsersModal) => { setIsOpen(true); form.reset({ ...user, dateOfBirth: user.dateOfBirth ? new Date(user.dateOfBirth) : undefined, identityExpiry: user.identityExpiry ? new Date(user.identityExpiry) : undefined, }); };
- 检查Zod校验规则
确保schema里的日期字段类型和表单存储的类型一致:
- 存储Date对象:
const UserSchema = z.object({ dateOfBirth: z.date(), identityExpiry: z.date(), // 其他字段 });
- 存储字符串:
const UserSchema = z.object({ dateOfBirth: z.string().datetime(), identityExpiry: z.string().datetime(), // 其他字段 });
修改后的Calendar组件代码片段
: (key === 'dateOfBirth' || key === 'identityExpiry') ? ( <div className=''> <Popover> <PopoverTrigger asChild> <Button variant="outline" className={cn( "w-full pl-3 text-left font-normal", !field.value && "text-muted-foreground" )} > {field.value ? ( format(typeof field.value === 'string' ? new Date(field.value) : field.value, "MMM dd, yyyy") ) : ( <span>Pick a date</span> )} <CalendarIcon className="ml-auto h-4 w-4 opacity-50" /> </Button> </PopoverTrigger> <PopoverContent className="w-auto p-0" align="start" onClick={(e) => e.stopPropagation()}> <Calendar mode="single" captionLayout="dropdown-buttons" selected={field.value ? (typeof field.value === 'string' ? new Date(field.value) : field.value) : undefined} onSelect={(date) => { if (date) field.onChange(date); }} fromYear={1960} toYear={2030} initialFocus /> </PopoverContent> </Popover> </div> )
内容的提问来源于stack exchange,提问作者Luswepo Silumbwe
相关产品推荐
相关产品推荐

