You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Hook Form、Zod与datetime-local输入框配合失效求解决方案

问题

我定义了如下Zod表单Schema:

const FormSchema = z.object({
  title: z.string().nonempty('A Title is required'),
  body: z.string().nonempty('A Message Body is required'),
  goTime: z.string().datetime()  //also tried .date()
})

我希望使用datetime-local类型的输入框来编辑goTime字段,但无论如何操作都无法让Schema验证与选择器正常协同工作。

我的useForm配置如下:

const {
  register,
  handleSubmit,
  watch,
  formState: { errors }
} = useForm<Inputs>({
  defaultValues: {
    title: title, //this is a string
    body: body || '', //this is a string?
    goTime: goTime?.toISOString().slice(0,16)  //this is the datetime field.
  }
})

输入框部分我尝试了多种配置:

<input
  type="datetime-local"
  // defaultValue={goTime?.toISOString().slice(0,16)}
  {...register('goTime', {
    setValueAs: (v) => new Date(v).toISOString().slice(0,16)
  })}
/>
{errors.goTime?.message && (
  <p className='text-sm text-red-400'>{errors.goTime.message}</p>
)}

所有尝试都无效,求一个完整可用的示例,要求从已有对象的时间戳设置输入框默认值,选择后通过验证并保存到数据库。


完整解决方案

核心问题是datetime-local输入框的格式与Zod默认验证规则不匹配,同时字段类型转换逻辑需要对齐。以下是完整可运行的示例:

1. 调整Zod Schema

datetime-local返回的格式是YYYY-MM-DDTHH:mm,而Zod的z.string().datetime()默认要求带时区的ISO格式(如2024-05-20T12:30:00.000Z)。我们可以自定义验证规则,同时添加转换逻辑直接生成适合数据库存储的标准ISO字符串:

import { z } from "zod";

// 推导表单字段类型
type Inputs = z.infer<typeof FormSchema>;

const FormSchema = z.object({
  title: z.string().nonempty('A Title is required'),
  body: z.string().nonempty('A Message Body is required'),
  goTime: z.string()
    // 验证datetime-local的格式合法性
    .refine(val => /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(val), 'Invalid datetime format')
    // 自动转换为标准ISO字符串,方便存入数据库
    .transform(val => new Date(val).toISOString())
});

2. 正确配置useForm

默认值必须严格符合datetime-local支持的格式,若原始goTime是Date对象或时间戳,需做对应转换:

import { useForm } from "react-hook-form";

// 假设从后端获取的初始时间(可以是Date对象或时间戳)
const initialGoTime = new Date(); // 示例:或传入1716183000000

const {
  register,
  handleSubmit,
  formState: { errors }
} = useForm<Inputs>({
  defaultValues: {
    title: "", // 替换为你的初始标题
    body: "", // 替换为你的初始内容
    // 将Date对象/时间戳转换为datetime-local支持的格式
    goTime: initialGoTime 
      ? new Date(initialGoTime).toISOString().slice(0, 16) 
      : ""
  }
});

3. 输入框组件实现

无需额外的setValueAs转换,直接注册输入框即可:

<form onSubmit={handleSubmit((data) => {
  // data.goTime已通过Zod转换为标准ISO字符串,可直接存入数据库
  console.log(data);
  // 调用API保存逻辑,例如:
  // fetch('/api/save', { method: 'POST', body: JSON.stringify(data) })
})}>
  <div>
    <label>Title:</label>
    <input {...register('title')} />
    {errors.title && <p className='text-sm text-red-400'>{errors.title.message}</p>}
  </div>

  <div>
    <label>Body:</label>
    <textarea {...register('body')} />
    {errors.body && <p className='text-sm text-red-400'>{errors.body.message}</p>}
  </div>

  <div>
    <label>Go Time:</label>
    <input
      type="datetime-local"
      {...register('goTime')}
    />
    {errors.goTime && <p className='text-sm text-red-400'>{errors.goTime.message}</p>}
  </div>

  <button type="submit">Submit</button>
</form>

关键注意点

  • datetime-local的输入/输出格式固定为YYYY-MM-DDTHH:mm,必须和默认值、验证规则完全对齐
  • 数据库存储的转换逻辑放在Zod的transform中完成,避免在输入框层做多余处理
  • 不要在register中添加setValueAs,否则会和Zod的转换逻辑冲突

内容的提问来源于stack exchange,提问作者Chris Valentine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 22:43:18