final-form、formik、react-final-form 和 redux-form 都是用于在 React 应用中管理表单状态、验证和提交逻辑的流行库。final-form 是一个框架无关的纯 JavaScript 表单状态引擎;formik 是一个专注于 React 开发体验的表单库,提供简洁的 API 和良好的 TypeScript 支持;react-final-form 是 final-form 的官方 React 绑定,结合了底层引擎的灵活性和 React 的声明式特性;redux-form 则将表单状态存储在 Redux store 中,但已被官方标记为弃用,不再推荐用于新项目。
在 React 应用中处理表单看似简单,但一旦涉及复杂验证、动态字段、性能优化或状态同步,就需要专业的表单管理方案。本文将深入比较 final-form、formik、react-final-form 和 redux-form 四个主流库的核心设计、API 风格和适用场景,帮助你在架构层面做出明智选择。
final-form 是一个纯 JavaScript 的表单状态管理引擎,不依赖任何 UI 框架。它通过订阅机制实现高效的状态更新,并支持细粒度的字段级重渲染控制。
// final-form: 纯 JS 引擎
import { createForm } from 'final-form';
const form = createForm({
onSubmit: values => console.log(values),
validate: values => {
const errors = {};
if (!values.email) errors.email = '必填';
return errors;
}
});
const unsubscribe = form.subscribe(state => {
console.log('表单状态变化:', state);
}, { values: true });
formik 是一个面向 React 的高阶组件(HOC)和自定义 Hook 方案,强调开发体验和约定优于配置。它将表单状态、验证、提交逻辑封装在组件内部,适合快速开发。
// formik: React 专用
import { useFormik } from 'formik';
function MyForm() {
const formik = useFormik({
initialValues: { email: '' },
validate: values => {
const errors = {};
if (!values.email) errors.email = '必填';
return errors;
},
onSubmit: values => alert(JSON.stringify(values, null, 2))
});
return (
<form onSubmit={formik.handleSubmit}>
<input
name="email"
onChange={formik.handleChange}
value={formik.values.email}
/>
{formik.errors.email ? <div>{formik.errors.email}</div> : null}
<button type="submit">提交</button>
</form>
);
}
react-final-form 是 final-form 的 React 绑定层,将底层引擎的能力以 React 组件形式暴露出来。它结合了 final-form 的灵活性和 React 的声明式 UI。
// react-final-form: 基于 final-form 的 React 封装
import { Form, Field } from 'react-final-form';
const MyForm = () => (
<Form
onSubmit={values => console.log(values)}
validate={values => {
const errors = {};
if (!values.email) errors.email = '必填';
return errors;
}}
render={({ handleSubmit, pristine, invalid }) => (
<form onSubmit={handleSubmit}>
<Field name="email">
{({ input, meta }) => (
<div>
<input {...input} placeholder="邮箱" />
{meta.error && meta.touched && <span>{meta.error}</span>}
</div>
)}
</Field>
<button type="submit" disabled={pristine || invalid}>
提交
</button>
</form>
)}
/>
);
redux-form 将整个表单状态存储在 Redux store 中,适用于已重度依赖 Redux 的项目。但因其全局状态管理方式,在大型应用中可能导致不必要的重渲染。
// redux-form: Redux 集成
import { reduxForm, Field } from 'redux-form';
const renderField = ({ input, meta: { touched, error } }) => (
<div>
<input {...input} />
{touched && error && <span>{error}</span>}
</div>
);
let ContactForm = props => {
const { handleSubmit } = props;
return (
<form onSubmit={handleSubmit}>
<Field name="email" component={renderField} />
<button type="submit">提交</button>
</form>
);
};
ContactForm = reduxForm({
form: 'contact', // 表单唯一标识
validate: values => {
const errors = {};
if (!values.email) errors.email = '必填';
return errors;
}
})(ContactForm);
final-form / react-final-form 采用字段级订阅机制。只有订阅了特定字段状态(如 value、error)的组件才会在该字段变化时重渲染。
// react-final-form: 字段级更新
<Field name="firstName" subscription={{ value: true }}>
{({ input }) => <input {...input} />} {/* 仅 firstName 变化时更新 */}
</Field>
<Field name="lastName" subscription={{ value: true }}>
{({ input }) => <input {...input} />} {/* 仅 lastName 变化时更新 */}
</Field>
formik 默认在任意字段变化时重渲染整个表单组件。虽然可通过 useField 或 React.memo 优化,但需要开发者主动处理。
// formik: 手动优化重渲染
const FirstNameField = () => {
const [field] = useField('firstName');
return <input {...field} />;
};
// 需要配合 React.memo 使用
const MemoizedFirstName = React.memo(FirstNameField);
redux-form 将所有字段状态存入 Redux store,导致任何字段变化都会触发连接到 store 的组件重渲染,除非使用 shouldComponentUpdate 或 reselect 进行精细控制。
💡 注意:根据 npm 页面和 GitHub 仓库信息,
redux-form已被官方标记为 deprecated(已弃用),不再推荐用于新项目。维护者建议迁移到react-final-form或其他现代方案。
所有库均支持同步验证,但在异步验证处理上存在差异。
final-form / react-final-form 通过返回 Promise 支持异步验证,并内置防抖(debounce)和取消机制。
// react-final-form: 异步验证
const asyncValidate = async value => {
const response = await fetch(`/api/validate?email=${value}`);
const json = await response.json();
return json.valid ? undefined : '邮箱已被占用';
};
<Field name="email" validate={asyncValidate}>
{/* ... */}
</Field>
formik 同样支持返回 Promise 的验证函数,但需注意其默认行为是每次输入都触发验证,可能需配合 validateOnBlur 或自定义防抖。
// formik: 异步验证
const validateEmail = async value => {
if (!value) return '必填';
const exists = await checkEmailExists(value);
if (exists) return '邮箱已被占用';
};
useFormik({
validate: values => ({
email: validateEmail(values.email)
})
});
redux-form 也支持异步验证,但因状态管理方式,在大型表单中可能引发性能问题。
处理动态字段(如可增删的列表项)时,各库表现如下:
final-form / react-final-form 提供 FieldArray 组件,支持嵌套字段和数组操作。
// react-final-form: 动态列表
import { FieldArray } from 'react-final-form-arrays';
<FieldArray name="friends">
{({ fields }) => (
<div>
{fields.map((name, index) => (
<Field key={name} name={name} component="input" />
))}
<button type="button" onClick={() => fields.push('')}>添加好友</button>
</div>
)}
</FieldArray>
formik 通过 useFieldArray Hook(需额外安装 formik 的数组支持)或手动管理数组状态实现。
// formik: 动态列表
import { useField, useFormikContext } from 'formik';
const FriendsField = () => {
const { values, setFieldValue } = useFormikContext();
const [field] = useField('friends');
return (
<div>
{values.friends.map((_, index) => (
<input
key={index}
{...field}
name={`friends[${index}]`}
value={values.friends[index]}
/>
))}
<button type="button" onClick={() => setFieldValue('friends', [...values.friends, ''])}>
添加好友
</button>
</div>
);
};
redux-form 也提供 FieldArray,但同样受制于全局状态更新问题。
final-form:框架无关,可轻松集成到任何状态管理方案中。formik:自包含状态,与外部状态管理(如 Redux、Zustand)需手动桥接。react-final-form:天然适配 React,也可通过自定义订阅器与 Redux 等集成。redux-form:深度绑定 Redux,若项目未使用 Redux 则引入成本过高。根据 npm 官方页面 和 GitHub 仓库,redux-form 已被明确标记为 deprecated。维护者指出:
“Redux Form is no longer maintained. We recommend using React Final Form or Formik instead.”
因此,新项目应避免使用 redux-form,现有项目应制定迁移计划。
| 场景 | 推荐方案 |
|---|---|
| 需要极致性能和细粒度控制 | final-form + 自定义 UI 绑定 |
| 快速开发、中小型表单、偏好约定优于配置 | formik |
已使用 final-form 或需要 React 声明式 API | react-final-form |
| 旧 Redux 项目且无法重构 | (仅限维护)redux-form(不推荐新项目) |
最终,选择应基于团队熟悉度、项目规模、性能要求和长期维护成本综合判断。对于大多数新项目,formik 和 react-final-form 是更安全、更现代的选择。
选择 final-form 如果你需要一个轻量级、框架无关的表单状态管理核心,且愿意自行构建 UI 绑定层。它适合对性能有极致要求或需要在非 React 环境中使用表单逻辑的场景,但会增加初始开发成本。
选择 formik 如果你追求快速开发体验、偏好简洁直观的 API,并且项目以中小型表单为主。它内置了大量实用功能(如字段数组、错误处理),TypeScript 支持良好,社区资源丰富,是大多数 React 项目的理想起点。
选择 react-final-form 如果你已经熟悉 final-form 的理念,或者需要在 React 中实现细粒度的字段级重渲染控制。它在性能和灵活性之间取得了良好平衡,特别适合包含大量字段或高频交互的复杂表单。
不要在新项目中选择 redux-form —— 它已被官方明确标记为弃用。仅在维护遗留 Redux 项目且短期内无法重构时考虑继续使用,长期应迁移到 react-final-form 或 formik。
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! 💰
✅ Zero dependencies
✅ Framework agnostic
✅ Opt-in subscriptions - only update on the state you need!
✅ 💥 5.1k gzipped 💥
Comprehensive JS framework and UI components for building enterprise-grade web apps.
In the interest of making 🏁 Final Form the best library it can be, we'd love your thoughts and feedback.
