今天我们用“报名表”这个真实案例,对比三种写法:纯 React 控制组件、React Hook Form、Formik。看完你能选出最适合自己团队的方案。

🎯 场景设定

报名表包含:

  • 姓名(必填)
  • 邮箱(格式校验)
  • 年龄(数字范围)
  • 兴趣(多选)
  • 是否同意协议(勾选)

我们要做到:数据收集清晰、验证好维护、性能不卡顿。

🧱 方案一:纯 React 控制组件

function PureForm() {
  const [values, setValues] = useState({
    name: '',
    email: '',
    age: '',
    hobbies: [],
    agree: false,
  });

  const handleChange = (field) => (event) => {
    const value = event.target.type === 'checkbox'
      ? event.target.checked
      : event.target.value;
    setValues((prev) => ({ ...prev, [field]: value }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    // 自己写校验
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={values.name} onChange={handleChange('name')} />
      {/* 其他字段 */}
    </form>
  );
}
  • 优点:无需额外依赖,逻辑透明。
  • 缺点:每个字段都要写 value/onChange,校验散落在各处,添加字段很麻烦。

⚙️ 方案二:React Hook Form(RHF)

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

function RhfForm() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    console.log('提交数据', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register('name', { required: '姓名必填' })}
        placeholder="姓名"
      />
      {errors.name && <p>{errors.name.message}</p>}

      <input
        {...register('email', {
          required: '邮箱必填',
          pattern: {
            value: /^[\w.-]+@[\w.-]+\.[A-Za-z]{2,}$/,
            message: '邮箱格式不正确',
          },
        })}
        placeholder="邮箱"
      />

      <input
        type="number"
        {...register('age', {
          min: { value: 18, message: '年龄需 ≥ 18' },
          max: { value: 60, message: '年龄需 ≤ 60' },
        })}
        placeholder="年龄"
      />

      <label>
        <input type="checkbox" {...register('agree', { required: true })} />
        同意协议
      </label>

      <button type="submit">提交</button>
    </form>
  );
}
  • 优点:
    • 默认使用原生表单事件,性能好。
    • 验证规则写在一起,结构清晰。
    • 支持 Resolver(Yup/Zod)统一管理校验。
  • 缺点:与 UI 库集成时需要配合 Controller

🧰 方案三:Formik

import { Formik, Form, Field, ErrorMessage } from 'formik';
import * as Yup from 'yup';

const schema = Yup.object({
  name: Yup.string().required('姓名必填'),
  email: Yup.string().email('邮箱格式不正确').required('邮箱必填'),
  age: Yup.number().min(18).max(60),
  agree: Yup.boolean().isTrue('请同意协议'),
});

function FormikForm() {
  return (
    <Formik
      initialValues={{ name: '', email: '', age: '', agree: false }}
      validationSchema={schema}
      onSubmit={(values) => console.log(values)}
    >
      {() => (
        <Form>
          <Field name="name" placeholder="姓名" />
          <ErrorMessage name="name" component="p" />

          <Field name="email" placeholder="邮箱" />
          <ErrorMessage name="email" component="p" />

          <Field name="age" type="number" />
          <ErrorMessage name="age" component="p" />

          <label>
            <Field name="agree" type="checkbox" /> 同意协议
          </label>
          <ErrorMessage name="agree" component="p" />

          <button type="submit">提交</button>
        </Form>
      )}
    </Formik>
  );
}
  • 优点:
    • 与 Yup 集成非常顺手。
    • 表单状态(dirty/ touched)管理清晰。
  • 缺点:
    • 默认是受控组件,字段多时性能略逊。
    • 需要配合 React.memoFastField 优化。

⚖️ 三者对比表

指标 纯 useState React Hook Form Formik
开发体验 简单但重复代码多 语法简洁,api 现代 结构清晰,集成 Yup 强
性能 字段多时易卡 高性能(非受控) 字段多时需优化
学习成本
社区生态 原生 活跃,社区 Hook 多 老牌,资料多
适用场景 小表单/简单需求 中大型表单、性能敏感 复杂验证、企业级项目

🧯 实战建议

  1. 简单弹窗表单 → useState + 自定义验证。
  2. 中大型表单、性能敏感 → React Hook Form。
  3. 复杂流程(步骤表单、动态校验) → Formik + Yup。

🚀 提升手感的小技巧

  • RHF:使用 devtools 插件观察表单状态。
  • Formik:使用 FieldArray 管理动态字段。
  • 所有方案都可以配合 yup/zod 写统一校验。

🏁 小练习

  1. 把同一个表单分别用三种方式实现,统计代码行数和提交性能。
  2. 用 RHF 的 Controller 集成 Ant Design 的 Select,记录注意事项。
  3. 写篇体验文:《我们团队从 Formik 迁移到 React Hook Form 的原因》。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐