
使用 WebSocket 提交 redux-form 表单手动掌控 submitting 状态与错误上报的完整指南【免费下载链接】redux-formA Higher Order Component using react-redux to keep form state in a Redux store项目地址: https://gitcode.com/gh_mirrors/re/redux-form导读redux-form原生通过 Promise 管理表单的submitting状态与提交错误但在实时聊天、协同编辑、长连接推送等 WebSocket 场景下提交结果往往以异步消息而非 Promise 返回值的形式到达。本文以仓库文档 docs/faq/WebsocketSubmit.md 为核心讲解如何利用redux-form导出的startSubmit/stopSubmitAction Creators手动驱动提交状态机并深入源码揭示其底层 Redux 动作与 reducer 的对应关系帮助你在任何异步范式下都能复用redux-form完整的提交状态体系。一、问题背景Promise 不是唯一的异步范式redux-form对表单提交的内建支持建立在 Promise 之上当你传入的onSubmit函数返回一个 Promise 时redux-form会在提交期间把submitting置为true在 Promise resolve 或 reject 后恢复见 docs/api/Props.md#submitting 中 This prop will only work if you have passed anonSubmitfunction that returns a promise 的说明。若 Promise 以SubmissionError拒绝错误会被填充到对应字段的errorprop 上_error键则作为表单级error呈现见 docs/api/Props.md#handleSubmit。这一设计在 HTTP 请求fetch/axios等返回 Promise场景下开箱即用但 WebSocket 是事件驱动的调用socket.send()后结果通过message事件异步返回中间不存在一个可 await 的 Promise 对象。此时若直接复用默认onSubmit流程submitting永远不会被正确翻转错误也无处安放。好在redux-form的设计者早已留好了解耦口提交状态本身就是由 Redux store 中的submitting、submitFailed、submitSucceeded、submitErrors等字段驱动的而这些字段只需两个动作就能手动控制——START_SUBMIT与STOP_SUBMIT。二、核心思路手动派发 START_SUBMIT / STOP_SUBMITredux-form将全部内部 Action Creators 导出允许你完全掌控要派发的任意动作见 docs/api/ActionCreators.md 开篇redux-formexports all of its internal action creators, allowing you complete control to dispatch any action you wish。其中与本主题直接相关的两个动作是Action Creator签名作用startSubmitstartSubmit(form: String)将指定表单的submitting标志置为truestopSubmitstopSubmit(form: String, errors: Object)将submitting置为false传入errors时填充各字段的submitErrors表单级错误使用_error键对应实现位于 src/actions.jsconst startSubmit: StartSubmit (form: string): StartSubmitAction ({ type: START_SUBMIT, meta: { form } }) const stopSubmit: StopSubmit ( form: string, errors: ?Object ): StopSubmitAction ({ type: STOP_SUBMIT, meta: { form }, payload: errors, error: !!(errors Object.keys(errors).length) })注意stopSubmit的error标志只要errors对象非空动作即被标记为error这是 redux 生态中约定俗成的错误传播方式也让redux-logger等中间件能直观地区分成功与失败的提交。三、实战代码WebSocket 提交的完整实现下面是在 WebSocket 场景下驱动redux-form提交状态的最小完整实现节选自 docs/faq/WebsocketSubmit.md 的官方示例import { startSubmit, stopSubmit } from redux-form function submitForm(data, dispatch) { // 告诉 redux-form提交已开始 dispatch(startSubmit(myFormName)) channels.methods.push(submit:myFormName, { data }) receive(ok, () { // 告诉 redux-form提交已结束成功 dispatch(stopSubmit(myFormName)) // ^ 如果你随后会重定向或做其他会销毁表单状态的操作 // 这步 stopSubmit 可以省略 }).receive(error, response { const { errors } response // 告诉 redux-form提交已结束携带错误 dispatch(stopSubmit(myFormName, errors)) }) }要点拆解startSubmit(myFormName)中的myFormName必须与reduxForm({ form: myFormName })装饰器注册的表单名完全一致因为动作的meta.form正是 reducer 定位表单状态切片的键见 src/actions.js 与 src/createReducer.js。成功分支stopSubmit(form)不传第二个参数reducer 会删除submitting、submitFailed、submitSucceeded、submitErrors等提交相关字段使表单恢复干净状态。失败分支stopSubmit(form, errors)传入服务端返回的错误对象形如{ field1: error1, field2: error2, _error: form-level error }reducer 会将其写入submitErrors并通过submitFailed: true暴露失败状态。可省略的stopSubmit如果提交成功后会跳转页面或destroy()表单表单状态随之销毁则无需再派发stopSubmit避免无意义的动作开销。四、底层原理reducer 如何消费这两个动作理解START_SUBMIT/STOP_SUBMIT在 reducer 中的处理逻辑是把握整个机制的关键。在 src/createReducer.js 中START_SUBMIT { return setIn(state, submitting, true) }, STOP_SUBMIT { let result state result deleteIn(result, submitting) result deleteIn(result, submitFailed) result deleteIn(result, submitSucceeded) if (payload Object.keys(payload).length) { const { _error, ...fieldErrors } payload if (_error) { result setIn(result, error, _error) } else { result deleteIn(result, error) } if (Object.keys(fieldErrors).length) { result setIn(result, submitErrors, fromJS(fieldErrors)) } else { result deleteIn(result, submitErrors) } result setIn(result, submitFailed, true) } else { result deleteIn(result, error) result deleteIn(result, submitErrors) } return result }可以总结出以下行为契约START_SUBMIT只做一件事把submitting置为true从而触发 UI 层按钮禁用、转圈提示等效果STOP_SUBMIT无条件清空submitting、submitFailed、submitSucceeded三个标志当payload即errors非空时_error键写入表单级error其余字段写入submitErrors并最终把submitFailed置为true当payload为空或未传时同时清除error与submitErrors表示提交成功。动作类型常量定义在 src/actionTypes.js统一使用redux-form/前缀避免与其他库的动作类型冲突。所有 action creator 均从包入口 src/index.js 导出包括startSubmit、stopSubmit、setSubmitFailed、setSubmitSucceeded等因此可以直接import { startSubmit, stopSubmit } from redux-form。五、两种接入方式onSubmit prop 与 handleSubmit 参数官方文档明确指出submitForm函数有两种接入表单的方式方式一作为onSubmitprop 传给装饰后的表单组件const MyForm reduxForm({ form: myFormName })(FormComponent) // 使用处 MyForm onSubmit{(data, dispatch) submitForm(data, dispatch)} /方式二在表单组件内部作为handleSubmit()的参数class FormComponent extends React.Component { render() { const { handleSubmit } this.props return ( form onSubmit{handleSubmit((data, dispatch) submitForm(data, dispatch))} {/* 表单字段 */} /form ) } }关于这两种方式的取舍与细节handleSubmit的完整行为说明见 docs/api/Props.md#handleSubmit它负责先执行同步与异步校验校验通过后才调用你的提交函数因此无论你采用哪种接入方式submitForm被调用时拿到的data一定是通过校验的数据。从源码看handleSubmit内部会走executeSubmit最终调用submit(values, dispatch, props)见 src/handleSubmit.js——这正解释了为什么submitForm的签名是(data, dispatch)第一个参数是表单当前值第二个参数是 Redux 的dispatch函数恰好满足dispatch(startSubmit(...))/dispatch(stopSubmit(...))的需求。需要说明的是如果你通过 WebSocket 方案手动管理提交状态不建议同时在onSubmit中返回 Promise否则会与内建机制产生双重状态管理的冲突官方推荐的做法是让submitForm不返回任何 Promise完全交由startSubmit/stopSubmit控制状态机。六、联动状态submitFailed / submitSucceeded / error 的呈现手动派发stopSubmit(form, errors)后表单组件会获得一系列可供 UI 消费的状态 props均来自 docs/api/Props.mdProp类型说明来源行submittingboolean提交中为true由START_SUBMIT置位、STOP_SUBMIT复位L249-L253submitFailedboolean提交失败为true成功提交后复位L237-L241submitSucceededboolean提交成功为true下次失败后复位L243-L247errorany表单级错误来自错误对象中的_error键L147-L151submitErrorsobject各字段的提交错误通过字段的error/meta.error呈现reducer 实现见 src/createReducer.js典型的 UI 反馈代码class FormComponent extends React.Component { render() { const { submitting, submitFailed, error } this.props return ( div {submitting p正在提交…/p} {submitFailed p style{{ color: red }}{error || 提交失败}/p} {/* 字段的 submitErrors 会通过 Field 的 meta.error 自动展示 */} /div ) } }得益于redux-form的 store 驱动设计只要START_SUBMIT/STOP_SUBMIT两个动作被正确派发submitting、submitFailed、submitSucceeded、字段级错误展示等全套机制都会像 Promise 路径一样自动生效——这正是手动复刻内建行为能够成立的根本原因。七、扩展用同样的手法接入其他异步范式WebSocket 只是其中一种场景这套手动派发提交状态动作的模式可以平移复制到任何非 Promise 的异步范式事件总线 / 消息队列bus.emit(submit, data)后通过once(success)/once(failure)监听回调派发stopSubmitSSEServer-Sent Events监听服务端推送的submit:done/submit:error事件后台任务轮询先startSubmit轮询任务状态接口得到终态后stopSubmit本地延迟模拟setTimeout模拟慢提交便于演示 loading 态。无论底层通道如何变化对外暴露的契约始终一致dispatch(startSubmit(formName)) // 提交开始 dispatch(stopSubmit(formName)) // 提交成功 dispatch(stopSubmit(formName, errors)) // 提交失败errors 形如 { field: msg, _error: ... }八、常见问题与注意事项表单名必须精确匹配startSubmit/stopSubmit的第一参数必须与reduxForm({ form })的表单名一致否则动作会被 reducer 分发到其他或不存在的表单切片上表现为submitting无变化。错误对象的结构字段级错误使用字段路径作键支持field.subfield或数组索引items[0].name表单级错误使用_error键这与SubmissionError、异步校验返回的错误结构完全兼容见 src/createReducer.js 的_error解构处理。重复提交防护startSubmit之后、stopSubmit之前submitting为true应据此禁用提交按钮防止用户重复点击产生多条 WebSocket 消息。成功路径的简化若提交成功后会destroy()表单或跳转离开可以省略stopSubmit(form)调用官方文档原文not necessary if you are redirecting or doing something else that will result in the form state being destroyed。与setSubmitFailed/setSubmitSucceeded的关系STOP_SUBMIT已经能同时驱动submitFailed/submitSucceeded的清除与置位见 src/createReducer.js在手动方案中一般无需再额外派发setSubmitFailed/setSubmitSucceeded其定义见 src/actions.js。九、小结redux-form的提交状态机本质上是动作驱动 store 存储的Promise 只是它默认喂给状态机的食物。通过startSubmit与stopSubmit这两个官方导出的 Action Creators你可以用 WebSocket、事件总线、SSE 等任何异步范式喂饱这台状态机从而完整复用submitting、submitFailed、submitSucceeded、submitErrors等全部能力。相关参考资源均位于本仓库官方 FAQ 原文docs/faq/WebsocketSubmit.mdAction Creators 完整签名与语义docs/api/ActionCreators.md表单 propshandleSubmit、submitting等说明docs/api/Props.mdstartSubmit/stopSubmit实现src/actions.jsreducer 对提交动作的处理src/createReducer.js包导出清单含全部 action creatorsrc/index.js【免费下载链接】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),仅供参考