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

如何在React Hook Form中正确处理动态对象数组表单?

React Hook Form + TypeScript + Zod + ShadCN 动态对象数组表单问题

我是React新手,正在使用React Hook Form(RHF)+TypeScript+Zod+ShadCN构建包含路线站点对象数组的动态表单。已成功实现简单文本及字符串数组字段,但在动态对象数组部分陷入困境。

已完成ShadCN Form组件的配置,使用useFieldArray处理字符串数组时一切正常,但处理对象数组时出现问题。在代码的City字段注释中尝试了多种方案:当使用{...form.register(stops.${index}.city)}时,能正确渲染默认值,但提交按钮无响应,验证会抛出undefined或误判错误,而其他字段功能正常。未收到控制台或编译错误,相关代码如下:

import "./styles.css";
import { zodResolver } from "@hookform/resolvers/zod";
import { useFieldArray, useForm } from "react-hook-form";
import * as z from "zod";

import { Button } from "./button";
import {
  Form,
  FormControl,
  FormDescription,
  FormField,
  FormItem,
  FormLabel,
  FormMessage,
} from "./form";
import { Input } from "./input";

const stopObj = z.object({
  order: z.coerce.number(),
  zip: z.string(),
  city: z.string({ required_error: "This field is required." }),
  state: z
    .string({ required_error: "This field is required." })
    .max(2, "2-letter state abbreviation"),
  country: z.coerce
    .string()
    .max(2, "Two-letter country code (ISO 3166-1 alpha-2)"),
});

const formSchema = z.object({
  firstName: z.string({ required_error: "This field is required." }),
  currency: z.enum(["USD", "CAD"]),
  stops: z
    .array(stopObj)
    .min(2, "2 stops minimum required (pickup and dropoff)"),
});

export default function App() {
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      firstName: "John",
      currency: "USD",
      stops: [
        { order: 1, city: "New York", state: "NY", zip: "02116", country: "US" },
        { order: 2, city: "Austin", state: "TX", zip: "12345", country: "US" },
      ],
    },
  });

  async function onSubmit(values: z.infer<typeof formSchema>) {
    console.log("onSubmit:", values);
  }

  const { fields } = useFieldArray({ name: "stops", control: form.control });

  return (
    <div className="bg-[#305645] w-full justify-center flex">
      <div className="w-3/4 bg-white px-5 py-3 my-20 rounded-lg">
        <Form {...form}>
          <div className="text-center font-medium">
            <h1 style={{ fontSize: "2rem", marginBottom: "1rem" }}>
              Complete Form
            </h1>
          </div>
          <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
            <div className="flex space-x-4 w-full">
              <div className="w-1/2">
                <FormField
                  control={form.control}
                  name="firstName"
                  render={({ field }) => (
                    <FormItem>
                      <FormLabel>First Name</FormLabel>
                      <FormControl>
                        <Input placeholder="John" {...field} />
                      </FormControl>
                      <FormMessage />
                    </FormItem>
                  )}
                />
              </div>

              <FormField
                control={form.control}
                name="currency"
                render={({ field }) => (
                  <FormItem>
                    <FormLabel>Currency</FormLabel>
                    <FormControl>
                      <Input placeholder="USD" {...field} />
                    </FormControl>
                    <FormDescription>USD or CAD.</FormDescription>
                    <FormMessage />
                  </FormItem>
                )}
              />
            </div>

            {/* FIXME how to correctly render and update each stop object?? */}
            {fields.map((field, index) => (
              <FormField
                control={form.control}
                key={field.id}
                name={`stops.${index}`}
                render={({ field }) => (
                  <>
                    <FormItem>
                      <FormLabel>City</FormLabel>
                      <FormDescription />
                      <FormControl>
                        <Input
                          {...field.value.city}
                          // {...form.register(`stops.${index}.city`)}
                          // defaultValue={field.name}
                          // value={field.value.city}
                          // onChange={(e) => field.onChange(e.target.value)}
                          // onBlur={(e) => field.onBlur(e.target.value)}
                        />
                      </FormControl>
                      <FormMessage />
                    </FormItem>

                    <FormItem>
                      <FormLabel>State</FormLabel>
                      <FormDescription />
                      <FormControl>
                        <Input
                          {...form.register(`stops.${index}.state`)}
                          // defaultValue={field.name}
                        />
                      </FormControl>
                      <FormMessage />
                    </FormItem>
                  </>
                )}
              />
            ))}

            <Button
              type="submit"
              className="bg-orange-500 font-extrabold w-full"
            >
              Submit
            </Button>
          </form>
        </Form>
      </div>
    </div>
  );
}

截图显示currency枚举字段正常工作,但city和state字段异常,且表现存在不一致性,恳请提供解决方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 19:17:06