final-form vs formik vs react-hook-form vs redux-form
React 表单状态管理库技术选型对比
final-formformikreact-hook-formredux-form类似的npm包:

React 表单状态管理库技术选型对比

final-formformikreact-hook-formredux-form 都是用于在 React 应用中管理表单状态、验证和提交逻辑的库。它们的目标是简化表单开发,减少样板代码,并提供一致的用户体验。redux-form 依赖 Redux 状态管理,formik 提供了基于组件的封装,final-form 是一个与框架无关的核心引擎,而 react-hook-form 则充分利用 React Hooks 实现高性能表单处理。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
final-form03,045382 kB9710 个月前MIT
formik034,385585 kB8355 个月前Apache-2.0
react-hook-form044,6321.28 MB1336 天前MIT
redux-form012,5151.45 MB4973 年前MIT

React 表单库深度对比:final-form、formik、react-hook-form 与 redux-form

在 React 开发中,表单处理看似简单,实则涉及状态同步、验证、错误反馈、性能优化等复杂问题。final-formformikreact-hook-formredux-form 各自提供了不同的解决思路。本文从真实工程角度出发,深入比较它们的核心机制、性能特征和适用场景。

⚠️ 维护状态警示

首先明确:redux-form 已被官方弃用。其 npm 页面和 GitHub 仓库均标注为 deprecated,建议仅用于遗留系统维护,新项目务必选择其他方案。其余三个库目前均处于活跃维护状态。

🧠 核心设计理念差异

redux-form:全局状态驱动(已弃用)

redux-form 将整个表单状态存储在 Redux store 中。每个字段的值、是否触碰、是否脏等信息都作为全局状态的一部分。这导致任何字段变更都会触发整个 Redux store 更新,进而可能引起无关组件的重渲染。

// redux-form (已弃用,仅作参考)
import { Field, reduxForm } from 'redux-form';

const MyForm = ({ handleSubmit }) => (
  <form onSubmit={handleSubmit}>
    <Field name="email" component="input" type="email" />
  </form>
);

export default reduxForm({ form: 'myForm' })(MyForm);

formik:组件状态封装

formik 将表单状态封装在组件内部(通过 useState 或类组件 state),并通过 Formik 组件或 useFormik Hook 暴露 API。它采用受控组件模式,所有字段值由 Formik 管理,通过 Field 组件或 getFieldProps 注入 props。

// formik
import { useFormik } from 'formik';

const MyForm = () => {
  const formik = useFormik({
    initialValues: { email: '' },
    onSubmit: values => alert(JSON.stringify(values, null, 2))
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <input
        id="email"
        name="email"
        type="email"
        onChange={formik.handleChange}
        value={formik.values.email}
      />
    </form>
  );
};

final-form:订阅式状态引擎

final-form 本身不绑定 React,它是一个纯 JavaScript 表单状态管理引擎。通过 react-final-form 包提供 React 绑定。其核心是“订阅”机制:只有订阅了特定字段状态的组件才会在该字段变化时重渲染。

// final-form
import { Form, Field } from 'react-final-form';

const MyForm = () => (
  <Form
    onSubmit={values => alert(JSON.stringify(values, null, 2))}
    render={({ handleSubmit }) => (
      <form onSubmit={handleSubmit}>
        <Field name="email">
          {({ input }) => <input {...input} type="email" />}
        </Field>
      </form>
    )}
  />
);

react-hook-form:非受控优先 + 智能注册

react-hook-form 默认采用非受控组件(uncontrolled components)模式,通过 register 方法将原生 DOM 元素注册到表单状态管理器。它只在必要时(如验证失败)才接管字段值,从而避免不必要的重渲染。

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

const MyForm = () => {
  const { register, handleSubmit } = useForm();

  return (
    <form onSubmit={handleSubmit(values => alert(JSON.stringify(values, null, 2)))}>
      <input {...register('email')} type="email" />
    </form>
  );
};

🚀 性能与重渲染行为

表单级重渲染

  • redux-form:任何字段变更触发整个 Redux store 更新,可能导致全应用重渲染(除非精心优化 selector)。
  • formik:默认情况下,任何字段变更会触发整个 Formik 组件及其子树重渲染。可通过 useFieldField 的细粒度订阅缓解,但需额外注意。
  • final-form:通过 subscription 选项,可精确控制哪些状态变化触发重渲染。例如,只订阅 submitting 状态的按钮不会因字段输入而重渲染。
  • react-hook-form:默认无重渲染 —— 字段值直接读取 DOM,仅在验证或提交时同步状态。使用 watch()Controller 时才会引入响应式更新。

字段级重渲染示例

假设有一个包含 10 个字段的表单,仅修改第一个字段:

  • formik(未优化):

    // 所有字段都会重渲染
    const formik = useFormik({ initialValues: { field1: '', /* ... */ } });
    return (
      <>
        <input {...formik.getFieldProps('field1')} />
        {/* 其他9个字段也会因 formik.values 变更而重渲染 */}
      </>
    );
    
  • final-form(优化后):

    // 仅 field1 的 Field 组件重渲染
    <Field name="field1">{({ input }) => <input {...input} />}</Field>
    // 其他 Field 组件不受影响
    
  • react-hook-form(默认):

    // 无任何 React 重渲染,值直接从 DOM 读取
    <input {...register('field1')} />
    // 其他字段完全不受影响
    

🔍 验证机制对比

同步验证

所有库都支持同步验证函数:

  • formik

    validate: values => {
      const errors = {};
      if (!values.email) errors.email = 'Required';
      return errors;
    }
    
  • final-form

    // 在 Field 上设置 validate
    <Field name="email" validate={value => !value ? 'Required' : undefined} />
    
  • react-hook-form

    // 通过 register 的 validate 选项
    <input {...register('email', { 
      required: 'Required',
      validate: value => value.includes('@') || 'Invalid email'
    })} />
    

异步验证

  • formik 支持 validate 返回 Promise,但需注意防抖和取消逻辑需自行处理。
  • final-formvalidate 函数可返回 Promise,内置防抖(通过 asyncValidate 配置)。
  • react-hook-formvalidate 函数可返回 Promise,但同样需自行处理竞态条件(如使用 resolver 集成 yupzod 可简化)。

🧩 复杂表单场景支持

动态字段(如数组、嵌套对象)

  • formik:通过 FieldArray 组件支持,API 直观但可能因数组变更导致大面积重渲染。

    <FieldArray name="friends">
      {({ push, remove, form }) => (
        <div>
          {form.values.friends.map((_, index) => (
            <input {...form.getFieldProps(`friends[${index}]`)} />
          ))}
          <button onClick={() => push('')}>Add</button>
        </div>
      )}
    </FieldArray>
    
  • final-form:需手动管理数组状态,但可通过 useField 和自定义逻辑实现,重渲染更可控。

  • react-hook-form:提供 useFieldArray Hook,专为动态字段设计,性能优秀。

    const { fields, append, remove } = useFieldArray({ name: 'friends' });
    return (
      <div>
        {fields.map((field, index) => (
          <input key={field.id} {...register(`friends.${index}`)} />
        ))}
        <button onClick={() => append('')}>Add</button>
      </div>
    );
    

表单间联动(如级联选择)

  • formik:通过 setFieldValueonChange 中手动更新关联字段。
  • final-form:利用 mutators 或在 onChange 中调用 form.change
  • react-hook-form:使用 setValue 更新关联字段,配合 watch 监听变化。

📦 与验证库集成

  • formik:常与 yup 配合,通过 validationSchema 选项。
  • final-form:可通过 final-form-calculate 或自定义 validate 集成 yup/joi
  • react-hook-form:官方推荐 resolver 选项,无缝集成 yupzodjoi 等。
    const { register } = useForm({
      resolver: yupResolver(schema)
    });
    

🛠️ 调试与开发者体验

  • formik:提供 debug prop 输出当前状态,DevTools 支持良好。
  • final-form:可通过 debug 选项输出内部状态变化。
  • react-hook-form:提供 devTool 插件,实时监控表单状态。

📊 总结:关键差异一览

特性final-formformikreact-hook-formredux-form
状态管理订阅式引擎组件内状态非受控 + 注册Redux 全局状态
重渲染控制精细(按字段订阅)默认粗粒度(可优化)极少(默认无)全局(需优化)
学习曲线中(需理解订阅)低(组件化)中(非受控思维)高(Redux 依赖)
动态字段手动实现FieldArrayuseFieldArrayFieldArray
验证集成灵活yup 为主多库 resolver灵活
维护状态活跃活跃活跃已弃用

💡 选型建议

  • 新项目首选 react-hook-form:性能最优,API 现代,社区活跃,适合大多数场景。
  • 偏好组件化 API 选 formik:快速原型开发、团队熟悉 render props 模式时效率高。
  • 需要极致性能控制选 final-form:大型表单、高频更新场景,愿意投入更多集成成本。
  • 绝对避免 redux-form:技术债风险高,性能瓶颈明显,社区已迁移。

最终,没有“最好”的库,只有“最适合”当前项目约束和团队习惯的方案。理解它们背后的设计权衡,才能做出明智的技术决策。

如何选择: final-form vs formik vs react-hook-form vs redux-form

  • final-form:

    选择 final-form 如果你需要一个轻量、与框架无关的表单状态管理核心,且愿意自行封装 React 绑定或已有自定义集成方案。它适合对性能敏感、需要精细控制订阅粒度的场景,但要求开发者处理更多底层细节,如字段注册和变更监听。

  • formik:

    选择 formik 如果你偏好基于组件的声明式 API,希望快速上手并获得开箱即用的表单功能(如验证、错误处理、嵌套字段)。它适合中小型项目或团队熟悉 React 组件模式,但需注意其基于 render props 的设计可能导致不必要的重渲染。

  • react-hook-form:

    选择 react-hook-form 如果你追求极致性能、最小化重渲染,并希望利用 React Hooks 的现代开发模式。它通过非受控组件和智能字段注册显著减少运行时开销,适合大型复杂表单或对性能有严格要求的应用,但需适应其非受控为主的哲学。

  • redux-form:

    不要在新项目中使用 redux-form,因为它已被官方标记为不再维护(deprecated)。现有项目应计划迁移到其他替代方案。Redux 社区已转向更轻量、专用的表单解决方案,避免将表单状态全局化带来的性能和维护成本。

final-form的README

You build great forms, but do you know HOW users use your forms? Find out with Form Nerd! Professional analytics from the creator of Final Form.


💰 Hey there! Do you fancy yourself a javascript expert? Take this quiz and get offers from top tech companies! 💰


🏁 Final Form

Final Form

Backers on Open Collective Sponsors on Open Collective NPM Version NPM Downloads Build Status codecov.io styled with prettier

Zero dependencies

✅ Framework agnostic

✅ Opt-in subscriptions - only update on the state you need!

✅ 💥 5.1k gzipped 💥


Final Form is sponsored by Sencha.

Comprehensive JS framework and UI components for building enterprise-grade web apps.


💬 Give Feedback on Final Form 💬

In the interest of making 🏁 Final Form the best library it can be, we'd love your thoughts and feedback.

Take a quick survey.


Get Started

Philosophy

Examples

API

Companion Libraries

Who's using Final Form?