- 前端
- UI组件
【免费下载链接】redux-form
A Higher Order Component using react-redux to keep form state in a Redux store
redux-form是一个基于 React 与 Redux 的表单状态管理方案,它通过高阶组件(Higher-Order Component)把 HTML 表单的全部状态——值、焦点、触碰状态、校验结果与提交状态——统一收进 Redux store 中统一管理。本文从项目 README 出发,结合仓库源码(src/index.js、src/createReducer.js、src/createReduxForm.js)与官方入门指南(docs/GettingStarted.md),系统讲解 redux-form 的定位、安装、四步上手流程、数据流原理与进阶能力,读完后你可以在自己的 React + Redux 项目中完整落地基于 store 的表单,并理解其底层 dispatch 与 reducer 的工作机制。
项目定位:表单状态也要“可订阅、可修改”
redux-form的官方定位非常明确:它配合 React Redux 工作,让 React 中的 HTML 表单能够借助 Redux 来存储自身的全部状态。仓库根目录的 README.md 用一句话概括了它的核心机制:这是一个 "A Higher Order Component using react-redux to keep form state in a Redux store"——即一个借助react-redux将表单状态保存在 Redux store 中的高阶组件库。
围绕这一机制,redux-form对外暴露了三个核心角色,它们在 docs/GettingStarted.md 中被明确划分了职责:
| 组成部分 | 类型 | 职责 |
|---|---|---|
formReducer | Reducer | 一个纯函数,根据应用中派发的 Redux action 告诉 store 如何更新表单状态 |
reduxForm() | 高阶组件 | 接收配置对象并返回一个新函数,用来包装你的表单组件,把用户交互绑定到 Redux action 的派发上 |
<Field/> | 组件 | 生活在被包装的表单组件内部,负责把各个输入组件与redux-form逻辑连接起来 |
三者分工清晰:formReducer负责“状态怎么变”,reduxForm()负责“把组件和 store 绑起来”,<Field/>负责“每个输入框怎么同步”。理解了这三者的关系,也就理解了整个库的设计骨架。
安装与版本前提
README 给出的安装方式非常简单:
npm install --save redux-form从仓库根目录的 package.json 可以看到当前版本为8.3.10,其 peerDependencies 声明的兼容范围是:react^16.4.2 || ^17.0.0 || ^18.0.0、react-redux^6.0.1 || ^7.0.0 || ^8.0.0、redux^3.7.2 || ^4.0.0,其中immutable(^3.8.2 || ^4.0.0)为可选依赖,仅在需要使用 Immutable.js 结构时才需要安装。安装前请确认你的 React / Redux 版本落在上述区间内。
官方提示:新项目是否应该使用 redux-form?
README 中有一段醒目的 “⚠️ ATTENTION ⚠️” 说明:如果你正处于项目起步阶段,社区的主流共识是不要把表单状态放进 Redux。redux-form 作者在维护过程中总结了表单场景的经验,另行构建了 React Final Form,并明确建议新项目优先考虑它(因为<Field>的组件 API 与 redux-form 高度相似,迁移成本很低)。作者认为,使用 redux-form 的唯一充分理由是:你确实需要把表单数据与 Redux 进行非常紧密的耦合——例如需要在距离表单组件很远的地方(比如另一个路由)订阅并修改这些表单数据。如果你的应用没有这种“远距离读写表单状态”的需求,应当避免引入 redux-form。
这段提示是选择技术方案时的重要参考,也是理解 redux-form 适用边界的核心信息:它是一个能力强大但定位明确的工具,适合"表单状态全局共享"的场景,而非所有表单场景的默认答案。
四步上手:从 store 到可提交的表单
docs/GettingStarted.md 给出了一条完整的四步上手路径,下面结合源码逐段展开。
第 1 步:把formReducer挂进 store
redux-form要求 store 必须知道如何处理来自表单组件的 action。做法是把formReducer通过combineReducers挂到根 reducer 上,并且必须挂在名为form的 key 下:
import { createStore, combineReducers } from 'redux' import { reducer as formReducer } from 'redux-form' const rootReducer = combineReducers({ // ...your other reducers here // you have to pass formReducer under 'form' key, // for custom keys look up the docs for 'getFormState' form: formReducer }) const store = createStore(rootReducer)formReducer服务于所有表单组件,整个应用只需挂载一次。若确实需要自定义挂载 key(比如使用redux-immutable时),则需要配合reduxForm()的getFormState配置项,其默认实现是state => getIn(state, 'form')(见 src/createReduxForm.js),即从根 state 的form字段读取表单状态。
从源码看,src/reducer.js 只是createReducer(plain)的一行封装,真正的主体是 src/createReducer.js。这个 createReducer 通过byForm包装实现"按表单名分发":每个 action 的meta.form决定了它作用于哪个表单切片,只有当 action 类型以@@redux-form/前缀开头(src/actionTypes.js 中定义prefix = '@@redux-form/')且带有meta.form时才会被处理,因此它不会干扰你的业务 reducer。
第 2 步:用reduxForm()包装表单组件
要让表单组件与 store 通信,需要用reduxForm()包装它。包装后会向组件注入关于表单状态的 props,以及处理提交流程的函数:
import React from 'react' import { Field, reduxForm } from 'redux-form' let ContactForm = props => { const { handleSubmit } = props return <form onSubmit={handleSubmit}>{/* form body*/}</form> } ContactForm = reduxForm({ // a unique name for the form form: 'contact' })(ContactForm) export default ContactFormform配置项是必填的,它必须是唯一的名字,用于在 store 中隔离不同表单的状态。如果()()双括号语法看起来费解,可以拆成两步:
// 第一步:创建“已配置”的函数 createReduxForm = reduxForm({ form: 'contact' }) // 第二步:用它包装 ContactForm 组件 ContactForm = createReduxForm(ContactForm)从实现上看,reduxForm在 src/reduxForm.js 中同样是createReduxForm(plain)的封装。在 src/createReduxForm.js 里可以看到一套默认配置,值得逐一了解其含义:
| 配置项 | 默认值 | 作用 |
|---|---|---|
touchOnBlur | true | 失焦时把字段标记为 touched |
touchOnChange | false | 值变化时把字段标记为 touched |
persistentSubmitErrors | false | 为true时,值变化后仍保留 submit 错误不自动清除 |
destroyOnUnmount | true | 组件卸载时销毁表单状态 |
enableReinitialize | false | initialValues变化时是否重新初始化表单 |
keepDirtyOnReinitialize | false | 重新初始化时是否保留脏字段的用户输入 |
updateUnregisteredFields | false | 重新初始化时是否更新未注册字段 |
getFormState | state => getIn(state, 'form') | 从根 state 定位表单状态切片的函数 |
pure | true | 是否启用 shouldComponentUpdate 深度比较优化 |
forceUnregisterOnUnmount | false | 为true时即使destroyOnUnmount为假也强制注销字段 |
submitAsSideEffect | false | 提交返回值作为 action 派发而非等待 Promise 结果 |
shouldAsyncValidate/shouldValidate/shouldError/shouldWarn | 各默认实现 | 控制异步/同步校验触发时机的判定函数 |
包装完成后,被包装组件会收到一整套注入 props,包括handleSubmit、values、pristine、dirty、valid、invalid、submitting、submitFailed、submitSucceeded、initialValues、initialize、reset、destroy、touch、untouch、blur、change、error、warning、asyncValidating等(具体可见 src/createReduxForm.js 中构建reduxFormProps的代码),这些 props 构成了你与表单状态交互的全部接口。
第 3 步:用<Field/>连接每个输入
<Field/>组件把每个输入控件连接到 store。基础用法:
<Field name="inputName" component="input" type="text" />这会创建一个type="text"的 HTML<input/>元素,同时自动传入value、onChange、onBlur等额外 props,用于在底层追踪和维护该输入的状态。
<Field/>远不止支持原生 input:component还可以是类组件或函数式(无状态)组件,name支持点号(profile.name)与数组下标(phones[0].number)形式来表示嵌套字段。完整的用法见 docs/api/Field.md。
把三个输入接上,一个完整的联系表单就成型了:
import React from 'react' import { Field, reduxForm } from 'redux-form' let ContactForm = props => { const { handleSubmit } = props return ( <form onSubmit={handleSubmit}> <div> <label htmlFor="firstName">First Name</label> <Field name="firstName" component="input" type="text" /> </div> <div> <label htmlFor="lastName">Last Name</label> <Field name="lastName" component="input" type="text" /> </div> <div> <label htmlFor="email">Email</label> <Field name="email" component="input" type="email" /> </div> <button type="submit">Submit</button> </form> ) } ContactForm = reduxForm({ // a unique name for the form form: 'contact' })(ContactForm) export default ContactForm从此刻起,store 就会根据表单组件派发的 action 被填充数据。字段的注册与注销由reduxForm内部通过REGISTER_FIELD/UNREGISTER_FIELDaction 完成(见 src/createReducer.js 的REGISTER_FIELD分支:每个字段会记录count计数以支持同名多实例),卸载组件时若destroyOnUnmount为真,状态会被清理。
第 4 步:响应提交
提交时,表单数据会以 JSON 对象的形式传给onSubmit函数:
import React from 'react' import ContactForm from './ContactForm' class ContactPage extends React.Component { submit = values => { // print the form values to the console console.log(values) } render() { return <ContactForm onSubmit={this.submit} /> } }提交流程的底层逻辑在 src/handleSubmit.js 中:先对全部字段执行touch(...fields)(所以校验错误在提交后立即显示),然后做异步校验,再进入executeSubmit。executeSubmit会调用submit(values, dispatch, props),并根据返回结果是 Promise 还是普通值走不同分支:Promise 成功则派发STOP_SUBMIT与SET_SUBMIT_SUCCEEDED,失败或抛错则派发STOP_SUBMIT、SET_SUBMIT_FAILED并触发可选的onSubmitFail回调;如果onSubmit同步抛出SubmissionError(src/SubmissionError.js),其errors会被解析并写入表单的 submit errors,而不会把异常直接抛给用户界面。
数据流:一次交互如何走完整个 store
docs/GettingStarted.md 用下图概括了 redux-form 的简化数据流,它同时也是 README 所描述的"表单状态存入 Redux"机制的具体化:
以一个被reduxForm()包装、内部含一个<Field/>文本输入的表单为例,数据流如下:
- 用户点击输入框;
- 派发 "Focus action"(即
FOCUS,类型为@@redux-form/FOCUS); formReducer更新对应的 state 切片;- 更新后的 state 再被传回输入组件。
其他任何交互——填写输入、改变状态、提交表单——都遵循同样的闭环。FOCUS分支在 src/createReducer.js 中的实现可见一斑:它会把上一个激活字段的active标记清除、记录新字段的visited与active、并在表单根节点写下active字段名。同理,CHANGE分支(src/createReducer.js)会写入或删除values.${field}、清空该字段的asyncErrors与(默认情况下的)submitErrors,并在touch为真时标记touched与anyTouched。
在大多数场景下,你不需要手动派发 action:reduxForm()已经把 action creators 绑定到 dispatch 上并注入为 props(src/createReduxForm.js 中通过bindActionCreators完成绑定),<Field/>内部也会自动调用注入的blur、change、focus。全部 34 个 action 类型(数组操作、校验、提交生命周期等)统一定义在 src/actionTypes.js 中,并全部从 src/index.js 导出,包括change、blur、focus、initialize、reset、destroy、touch、untouch、arrayPush、arrayRemove、setSubmitFailed、stopSubmit等。
进阶能力与文档导航
README 将完整的 API 与 FAQ 文档汇总如下,仓库内的对应文件为:
- 入门指南 Getting Started:本文四步上手的完整出处;
- API 文档:包含
reduxForm()的全部配置项(docs/api/ReduxForm.md)、<Field/>(docs/api/Field.md)、<FieldArray/>(docs/api/FieldArray.md)、<Fields/>(docs/api/Fields.md)、<Form/>(docs/api/Form.md)、<FormSection/>(docs/api/FormSection.md)、action creators(docs/api/ActionCreators.md)与 Reducer 插件机制(docs/api/ReducerPlugin.md); - FAQ:覆盖"提交函数没被调用""自定义输入组件""
handleSubmit与onSubmit的区别""提交后如何清空表单""React Native 与 Immutable.js 兼容性""如何减小打包体积"等 11 个高频问题; - 版本历史与迁移、MigrationGuide.md。
常见进阶方向,官方都给出了明确答案:
- 表单校验:
validate(同步校验,表单级)与warn(警告)两个配置项在 src/createReduxForm.js 中被合并执行;字段级校验通过在<Field>上配置validate/warnprops 实现,generateValidator会把所有字段级校验合并为一个校验函数; - 动态表单 / 字段数组:
<FieldArray/>配合arrayprops 中的push、pop、remove、swap、insert等 10 个数组操作方法(对应 src/createReducer.js 中 10 个数组 action 分支,它们会同步维护values、fields、syncErrors、syncWarnings、submitErrors、asyncErrors六个切片的一致性)创建可增删的动态字段列表; - Immutable.js:仓库提供
immutable入口(immutable.js 指向构建产物lib/immutable),源码中对应 src/immutable 目录,所有 reducer、Field、reduxForm均基于 Immutable 结构重新导出,数据操作走 src/structure/immutable 的getIn/setIn/splice实现; - 从 state 初始化 / 选择表单值:
initialValues配置项配合enableReinitialize、keepDirtyOnReinitialize控制初始化行为(src/createReducer.js 中INITIALIZE分支对 keepDirty、keepValues 等语义有详细实现与注释);formValueSelector与getFormValues等选择器从 src/selectors 目录导出,用于在表单之外读取 store 中的表单状态——这正是 README 所说"在远离表单组件的地方订阅并修改表单数据"场景的落点。
从源码看整体架构
从 src/index.js 的导出清单可以完整看到这个库的公共 API 面:reduxForm、reducer、Field、FieldArray、Fields、Form、FormSection、FormName、SubmissionError、formValueSelector、formValues、values、propTypes、34 个 action creators 以及isDirty/isValid/isPristine等 15 个状态选择器。
在实现层面,redux-form 通过**结构抽象(structure)**同时支持 plain 对象与 Immutable:所有底层读写(getIn/setIn/deleteIn/deepEqual/splice)都定义在 src/structure/plain 与 src/structure/immutable 中,createReducer、createReduxForm、createField都接收 structure 参数(src/reducer.js、src/reduxForm.js、src/Field.js 分别以plain调用),因此同一套逻辑可以无差别地运行在两种数据结构上。
总结
redux-form用"一个 reducer + 一个高阶组件 + 一个 Field 组件"三个构件,把 HTML 表单的全部状态收编进 Redux store,并借助@@redux-form/前缀的 34 种 action 与按表单名分发的 reducer 机制,实现了输入、焦点、校验、提交的完整状态闭环。它的核心价值在于"表单状态可被全局订阅与修改":当你的应用确实需要跨路由、跨组件紧密操作表单数据时,它是一套经过充分验证的方案;反之,README 的官方建议是优先考虑 React Final Form 等更轻量的替代方案。上手时只需记住四步:挂formReducer→ 用reduxForm()包装 → 用<Field/>连接输入 → 用onSubmit接收数据,然后即可按需深入到校验、字段数组、Immutable 与选择器等进阶能力中。
- 前端
- UI组件
【免费下载链接】redux-form
A Higher Order Component using react-redux to keep form state in a Redux store
相关推荐
redux-form 入门指南:用 formReducer、reduxForm() 与 Field 把表单状态接入 Redux Store
redux form 入门指南:用 formReducer、reduxForm 与 Field 把表单状态接入 Redux Store 本指南是 redux f
前端UI组件pm-skills 多视角头脑风暴命令 /brainstorm 完全指南:为现有与新产品系统化生成创意与实验设计
pm skills 多视角头脑风暴命令 /brainstorm 完全指南:为现有与新产品系统化生成创意与实验设计 /brainstorm 是 pm skills
前端UI组件redux-form reducer 完全指南:在 Redux Store 中挂载与扩展表单状态
redux form reducer 完全指南:在 Redux Store 中挂载与扩展表单状态 redux form 是一个通过 react redux 将表
前端UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考