ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

redux-form `formValueSelector` 深度指南:从 Redux 状态中优雅提取表单值

redux-form `formValueSelector` 深度指南:从 Redux 状态中优雅提取表单值 redux-formformValueSelector深度指南从 Redux 状态中优雅提取表单值【免费下载链接】redux-formA Higher Order Component using react-redux to keep form state in a Redux store项目地址: https://gitcode.com/gh_mirrors/re/redux-formformValueSelector是 redux-form 提供的选择器SelectorAPI专门用于简化通过connect()从 Redux store 中读取表单字段值的操作只需传入表单名即可获得一个可直接注入mapStateToProps的选择函数按字段名取出对应值。本指南将完整讲解其参数、返回结构、三种典型用法并结合本仓库源码createFormValueSelector.js、formValueSelector.spec.js剖析其底层实现与边界行为帮助你写出更简洁、可维护的表单值读取代码。一、它解决什么问题在 redux-form 中所有表单状态字段值values、元信息fields、错误errors等都保存在 Redux store 的state.form[formName]下。如果直接在mapStateToProps中手写取值逻辑通常需要这样connect(state ({ firstValue: state.form.myFormName.values.first, secondValue: state.form.myFormName.values.second }))(MyFormComponent)formValueSelector将这些按表单名 字段名取值的样板逻辑封装为一个可复用的函数。官方文档将其定位为a selector API to make it easier toconnect()to form values——它创建一个接受字段名、并从指定表单中返回对应值的选择函数。const selector formValueSelector(myFormName) connect(state ({ firstValue: selector(state, first), secondValue: selector(state, second) }))(MyFormComponent)从源码看该 API 的真正实现位于 createFormValueSelector.js是一个工厂函数通过注入不同的数据结构实现plain或immutable来支持两种状态结构src/formValueSelector.js默认导出注入plain普通 JS 对象/数组结构src/immutable/formValueSelector.js注入immutableImmutable.js 数据结构结构。两者最终统一通过redux-form的主入口src/index.js 中的export { default as formValueSelector }暴露给使用者。二、导入方式文档提供了 ES5 与 ES6 两种导入方式var formValueSelector require(redux-form).formValueSelector // ES5import { formValueSelector } from redux-form // ES6此外若你使用 Immutable.js 版本的状态结构redux-form 还提供了独立的入口文件见 immutable.js可从中导入formValueSelector内部指向 src/immutable/formValueSelector.js。三、参数详解3.1form : String必填要连接的表单名称。它必须与你在reduxForm()配置中传给表单的form值完全一致参见 ReduxForm 配置文档。源码层面的强制校验createFormValueSelector.jsinvariant(form, Form value must be specified)如果省略form或传入空值会直接抛出Form value must be specified错误。对应测试见 formValueSelector.spec.js。3.2getFormState : Function可选默认情况下redux-form 的 reducer 被挂载在state.form。如果你的应用通过reduxForm()的getFormState()配置参数将 reducer 挂载到了其他位置那么此处必须传入完全相同的函数用于定位 Redux store 中 redux-form reducer 所在的切片。默认值为state state.form。源码实现createFormValueSelector.jsconst nonNullGetFormState: GetFormState getFormState || (state getIn(state, form))注意这里使用的是getIn(state, form)而非state.form这是为了兼容 Immutable.js 结构getIn由注入的 structure 提供。测试用例验证了自定义挂载点的行为formValueSelector.spec.jsconst selector formValueSelector(myForm, state getIn(state, otherMountPoint)) const state fromJS({ otherMountPoint: { myForm: { values: { foo: bar } } } }) expect(selector(state, foo)).toBe(bar) // 从自定义挂载点成功取值使用前提只有当你在reduxForm()配置中设置了getFormState时才需要传这个参数否则请保持默认。两者不一致会导致取值失败。四、返回的 Selector 函数结构formValueSelector()返回一个函数其签名在 Flow 类型中定义为formValueSelector.types.js.flowtype FormValueSelectorInterface ( state: any, ...fields: string[] ) any4.1state : Object必填全局 Redux state通常在mapStateToProps中作为第一个参数传入。4.2...field : String必填要选择的字段名支持一个或多个只传一个字段名返回该字段的值可以是任意类型包括对象、数组传入多个字段名返回一个字段名 → 值的映射对象深层字段字段名中包含一个或多个.时返回结构也会保持嵌套深度。例如字段a.b与a.c结果为{ a: { b: bValue, c: cValue } }。如果未传任何字段源码会直接抛错createFormValueSelector.jsinvariant(fields.length, No fields specified)对应测试formValueSelector.spec.js。五、三种典型用法5.1 逐个字段独立取值connect(state ({ firstValue: selector(state, first), secondValue: selector(state, second) }))(MyFormComponent)适合字段之间无关联、希望各自独立成为 prop 的场景。5.2 多字段分组取值connect(state ({ myValues: selector(state, first, second) }))(MyFormComponent)适合把一组字段聚合成一个嵌套对象 prop。源码中的多字段分支createFormValueSelector.jsfields.reduce((accumulator, field) { const value getIn(nonNullGetFormState(state), ${form}.values.${field}) return value undefined ? accumulator : plain.setIn(accumulator, field, value) }, {})值得注意的两个实现细节深层路径还原plain.setIn见 src/structure/plain/setIn.js会把a.b这样的点路径还原为嵌套对象{ a: { b: ... } }与文档描述的深层结构行为一致跳过 undefined 值当某个字段值为undefined时该字段会被跳过不会出现在返回对象中累积器保持不变。因此当表单或字段完全不存在时多字段选择返回的是{}而不是包含undefined的对象见测试 formValueSelector.spec.js。而单字段分支则直接返回原始值不包装、不跳过return getIn(nonNullGetFormState(state), ${form}.values.${fields[0]})所以当状态缺失时单字段选择返回undefined见测试 formValueSelector.spec.js。5.3 直接把 Selector 当作mapStateToProps如果除了表单值你不需要从 state 中取其他任何 props且你选择的是多个字段那么选择函数本身就可以直接作为mapStateToProps使用connect(state selector(state, first, second))(MyFormComponent)注意这种方式要求返回一个对象即多字段模式如果只选择单个字段mapStateToProps会收到一个原始值而非对象这是无效的因此官方文档明确标注此用法适用于selecting multiple fields。六、深层字段与数组源码与测试实证6.1 深层字段取值单字段模式下getInsrc/structure/plain/getIn.js使用 lodash 的toPath将dog.cat.ewe.pig拆分为路径数组再逐级访问对象。测试验证了四层深度的取值formValueSelector.spec.jsconst state fromJS({ form: { myForm: { values: { dog: { cat: { ewe: { pig: Napoleon } } } } } } }) expect(selector(state, dog.cat.ewe.pig)).toBe(Napoleon)6.2 多字段深层结构合并多个深层字段会被setIn合并成完整嵌套结构即使它们来自不同的分支formValueSelector.spec.jsexpect(selector(state, dog.cat.ewe.pig, dog.rat.hog)).toEqual({ dog: { cat: { ewe: { pig: Napoleon } }, rat: { hog: Wilbur } } })6.3 数组值数组可以整体作为字段值返回。普通结构下直接返回数组Immutable 结构下返回 Immutable ListformValueSelector.spec.jsexpect(selector(state, mice)).toEqualMap([Jaq, Gus, Major, Bruno])深层数组字段路径与数组组合同样受支持见 formValueSelector.spec.js。七、与formValues/getFormValues的分工在 redux-form 生态中读取表单值还有另外两个 API理解它们的分工有助于选择正确工具formValueSelector(form, getFormState?)(state, ...fields)本文主角按字段名选择性取值支持深层路径与多字段合并适合与connect搭配做细粒度取值formValues(...fields)HOC 形式将字段值以 props 形式注入组件见 src/formValues.js 及文档 FormValues.mdgetFormValues(form)选择整个表单的values对象而非单个字段见 src/getFormValues.js 及文档 Selectors.md。如果你的需求是取一部分字段且关注深层结构formValueSelector是最贴合的选择。八、常见边界与注意事项表单名必须匹配form参数需与reduxForm({ form: myFormName })配置一致否则返回undefined或{}。单字段返回原始值selector(state, first)返回value不是{ first: value }只有多字段才返回对象。测试中分别用toBe与toEqual验证了这两种形态。缺失状态的返回值差异状态缺失时单字段返回undefined多字段返回{}。若想区分字段值为 undefined与字段不存在需要结合元信息如 getFormMeta自行判断。getFormState必须与 reducer 挂载点一致自定义挂载时这里传的函数要和reduxForm()配置中的一致否则找不到表单切片。Immutable.js 用户请使用独立入口普通入口基于plain结构直接对 Immutable Map 调用会失败请从 immutable.js 入口导入对应 src/immutable/formValueSelector.js。九、小结formValueSelector用一行代码解决了从 Redux 中按需读取表单字段值的重复劳动const selector formValueSelector(formName)随后即可在mapStateToProps中按字段名取单个值、按组取合并对象甚至直接充当mapStateToProps。其底层实现createFormValueSelector.js围绕getIn/setIn两个结构原语展开天然支持点路径深层字段、多字段合并、数组值以及自定义状态挂载点并有完整的双结构测试formValueSelector.spec.js保证 plain 与 Immutable.js 两种形态的行为一致。掌握它你的表单组件与 Redux 的连接将更加声明式、可读与可复用。【免费下载链接】redux-formA Higher Order Component using react-redux to keep form state in a Redux store项目地址: https://gitcode.com/gh_mirrors/re/redux-form创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表