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

Next.js13+TS中如何在Formik的Form内使用两个FieldArray?

你之前对FieldArray的用法误解了——它完全可以放在Form标签内部,不需要把整个Form包裹进去。每个需要数组类型的字段单独用FieldArray对应处理就行,同时支持添加、删除操作,具体修改步骤如下:


1. 修正初始值与TypeScript类型

首先把allergiesName和medicalConditionsName从字符串改成空数组,并定义对应TS类型确保类型安全:

type MedicalFormValues = {
  allergiesName: string[];
  medicalConditionsName: string[];
  emergencyContactName: string;
  emergencyContactPhone: string;
};

// Formik初始值
initialValues: MedicalFormValues = {
  allergiesName: [],
  medicalConditionsName: [],
  emergencyContactName: "",
  emergencyContactPhone: "",
}

2. 调整FieldArray的正确用法

每个FieldArray需要接收一个渲染函数,用来获取push(添加项)、remove(删除项)等操作方法,然后遍历数组渲染输入框,同时添加操作按钮。以allergiesName为例:

<FieldArray name="allergiesName">
  {({ push, remove }) => (
    <div className="mb-4">
      <label className="text-[12px] font-bold block mb-1">Allergies</label>
      {/* 遍历现有数组项渲染输入框 */}
      {values.allergiesName.map((_, index) => (
        <div key={index} className="flex gap-2 mb-2">
          <Field
            required
            type="text"
            name={`allergiesName[${index}]`}
            className="rounded-[10px] inputFieldBg border-0 text-sm focus:ring-blue-500 focus:border-blue-500 block flex-1 p-2.5"
            autoComplete="additional-name"
          />
          <button
            type="button"
            onClick={() => remove(index)}
            className="text-red-500 self-center"
          >
            删除
          </button>
        </div>
      ))}
      {/* 添加新项按钮 */}
      <button
        type="button"
        onClick={() => push("")}
        className="text-blue-500 text-sm mt-1"
      >
        + 添加过敏项
      </button>
    </div>
  )}
</FieldArray>

medicalConditionsName字段做完全相同的修改即可。

3. 更新验证Schema

如果使用Yup做表单验证,需要把两个数组字段的规则改成数组类型:

import * as Yup from 'yup';

const SignUpSchema = Yup.object().shape({
  allergiesName: Yup.array()
    .of(Yup.string().required("过敏项不能为空"))
    .min(1, "至少添加一个过敏项"), // 可选,根据业务需求调整
  medicalConditionsName: Yup.array()
    .of(Yup.string().required("病史项不能为空"))
    .min(1, "至少添加一个病史项"),
  emergencyContactName: Yup.string().required("紧急联系人姓名不能为空"),
  emergencyContactPhone: Yup.string().required("紧急联系人电话不能为空"),
});

完整修改后的代码(Next.js 13 AppRouter适配)

注意添加'use client'指令,因为Formik是客户端组件:

'use client';
import { Formik, Form, Field, FieldArray } from 'formik';
import * as Yup from 'yup';

type MedicalFormValues = {
  allergiesName: string[];
  medicalConditionsName: string[];
  emergencyContactName: string;
  emergencyContactPhone: string;
};

const SignUpSchema = Yup.object().shape({
  allergiesName: Yup.array()
    .of(Yup.string().required("过敏项不能为空"))
    .min(1, "至少添加一个过敏项"),
  medicalConditionsName: Yup.array()
    .of(Yup.string().required("病史项不能为空"))
    .min(1, "至少添加一个病史项"),
  emergencyContactName: Yup.string().required("紧急联系人姓名不能为空"),
  emergencyContactPhone: Yup.string().required("紧急联系人电话不能为空"),
});

export default function MedicalForm() {
  return (
    <Formik
      validationSchema={SignUpSchema}
      initialValues={
        {
          allergiesName: [],
          medicalConditionsName: [],
          emergencyContactName: "",
          emergencyContactPhone: "",
        } as MedicalFormValues
      }
      onSubmit={(values) => {
        // 提交时values中的两个字段已为数组类型
        console.log('提交数据:', values);
      }}
    >
      {({ values }) => (
        <div className="text-white w-[90%] formRegisterBg self-center flex flex-col px-4 pt-2 pb-6 rounded-[15px] form1">
          <span className="text-[20px] font-bold mt-2 mb-3">Medical Information</span>
          <Form>
            {/* 过敏项数组 */}
            <FieldArray name="allergiesName">
              {({ push, remove }) => (
                <div className="mb-4">
                  <label className="text-[12px] font-bold block mb-1">Allergies</label>
                  {values.allergiesName.map((_, index) => (
                    <div key={index} className="flex gap-2 mb-2">
                      <Field
                        required
                        type="text"
                        name={`allergiesName[${index}]`}
                        className="rounded-[10px] inputFieldBg border-0 text-sm focus:ring-blue-500 focus:border-blue-500 block flex-1 p-2.5"
                        autoComplete="additional-name"
                      />
                      <button
                        type="button"
                        onClick={() => remove(index)}
                        className="text-red-500 self-center"
                      >
                        删除
                      </button>
                    </div>
                  ))}
                  <button
                    type="button"
                    onClick={() => push("")}
                    className="text-blue-500 text-sm mt-1"
                  >
                    + 添加过敏项
                  </button>
                </div>
              )}
            </FieldArray>

            {/* 病史项数组 */}
            <FieldArray name="medicalConditionsName">
              {({ push, remove }) => (
                <div className="mb-4">
                  <label className="text-[12px] font-bold block mb-1">Medical Conditions</label>
                  {values.medicalConditionsName.map((_, index) => (
                    <div key={index} className="flex gap-2 mb-2">
                      <Field
                        required
                        type="text"
                        name={`medicalConditionsName[${index}]`}
                        className="rounded-[10px] inputFieldBg border-0 text-sm focus:ring-blue-500 focus:border-blue-500 block flex-1 p-2.5"
                        autoComplete="additional-name"
                      />
                      <button
                        type="button"
                        onClick={() => remove(index)}
                        className="text-red-500 self-center"
                      >
                        删除
                      </button>
                    </div>
                  ))}
                  <button
                    type="button"
                    onClick={() => push("")}
                    className="text-blue-500 text-sm mt-1"
                  >
                    + 添加病史项
                  </button>
                </div>
              )}
            </FieldArray>

            {/* 紧急联系人姓名 */}
            <div className="mb-4">
              <label
                htmlFor="emergencyContactName"
                className="text-[12px] font-bold block mb-1"
              >
                Emergency Contact Name
              </label>
              <Field
                required
                type="text"
                id="emergencyContactName"
                name="emergencyContactName"
                className="rounded-[10px] inputFieldBg border-0 text-sm focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
                autoComplete="additional-name"
              />
            </div>

            {/* 紧急联系人电话 */}
            <div className="mb-4">
              <label
                htmlFor="emergencyContactPhone"
                className="text-[12px] font-bold block mb-1"
              >
                Emergency Contact Phone
              </label>
              <Field
                required
                type="text"
                id="emergencyContactPhone"
                name="emergencyContactPhone"
                className="rounded-[10px] inputFieldBg border-0 text-sm focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5"
                autoComplete="additional-name"
              />
            </div>

            {/* 提交按钮 */}
            <button
              type="submit"
              className="bg-blue-500 text-white rounded-[10px] p-2.5 mt-2"
            >
              提交
            </button>
          </Form>
        </div>
      )}
    </Formik>
  );
}

关键说明

  • FieldArray可以直接嵌入Form内部,每个数组字段单独用一个FieldArray包裹对应的输入区域即可,无需嵌套整个Form。
  • 渲染函数中的push方法用于向数组添加新的空输入项,remove方法用于删除指定索引的项。
  • Field的name属性必须写成数组名[索引]的格式,Formik才能正确绑定数组中的每一个元素。
  • TypeScript类型定义确保初始值与表单值的类型一致,避免类型错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 08:04:52