在 dva 应用中集成 redux-undo:基于 onReducer 增强器的撤销/重做实战
在 dva 应用中集成 redux-undo基于 onReducer 增强器的撤销/重做实战【免费下载链接】dva React and redux based, lightweight and elm-style framework. (Inspired by elm and choo)项目地址: https://gitcode.com/gh_mirrors/dv/dva导读本文围绕仓库中 examples/with-redux-undo 示例讲解如何在 dva 应用中集成redux-undo实现撤销Undo与重做Redo能力。你将掌握 dvaonReducer配置项的正确用法、被undoable高阶 reducer 包裹后的状态树结构变化以及视图层如何通过ActionCreators.undo()/ActionCreators.redo()触发时间旅行并深入理解这一机制在 dva-core 中的底层实现。示例概览一个支持撤销重做的计数器with-redux-undo是一个麻雀虽小五脏俱全的 dva 示例它构建了一个经典的 Counter 计数器应用在原有/-增减操作之上额外提供了Undo与Redo两个按钮让每一次计数操作都能被回放或回退。示例的核心文件如下文件作用src/index.js应用入口dva 实例创建、onReducer配置、组件挂载src/models/counter.jscounter 模型定义add/minus两个 reducersrc/index.htmlHTML 入口包含#root挂载点并引入index.jspackage.json项目依赖与启动脚本从 package.json 可以看到示例的技术栈为dva^2.2.3、redux-undo^0.6.1、react^16.2.0并使用parcel-bundler^1.4.1作为零配置打包器。快速开始安装、启动与访问按照 README.md 的指引先安装依赖npm install或者使用 yarnyarn安装完成后启动开发服务器npm start或者yarn startpackage.json 中start脚本实际执行的是parcel ./src/index.html即由 Parcel 直接以src/index.html为入口进行打包与热更新。启动成功后打开浏览器访问http://localhost:1234即可看到 Counter 应用点击/-修改计数值点击Undo/Redo回退或重放历史操作。核心原理为什么需要onReducerdva 中一个 model 的 reducer 会被合并进 Redux 的 reducer 树状态以namespace为键组织。而redux-undo的核心机制是用一个高阶 reducer 包裹目标 reducer自动维护past历史、present当前、future未来三段状态从而实现时间旅行。在 dva 中接入redux-undo最简洁的入口就是实例化时传入的onReducer配置项——它是一个reducer 增强器在应用层拿到合并后的总 reducer之后、交给 Redux store 之前进行再加工。src/index.js 中正是这样做的const app dva({ onReducer: reducer (state, action) { const newState undoable(reducer, {})(state, action); return { ...newState }; }, });这里的reducer参数就是 Redux 的combineReducers合并结果包含 router、counter 等所有 namespace。undoable(reducer, {})将其整体包装为支持撤销/重做的高阶 reducer第二个参数{}为 redux-undo 的配置项示例使用空对象即默认配置。注意返回时{ ...newState }做了浅拷贝展开避免直接共享被包装后的引用。源码级剖析onReducer在 dva 内部如何生效理解onReducer的底层调用链有助于写出正确的增强器。以当前仓库 dva-core 的实现为准在start()中dva 通过plugin.get(onReducer)取出所有注册的 reducer 增强器packages/dva-core/src/index.js#L184createReducer()先通过combineReducers({ ...reducers, ...extraReducers, ...asyncReducers })合并出基础 reducerpackages/dva-core/src/index.js#L241-L249合并结果被reducerEnhancer(...)包裹后才交给createStore创建 storepackages/dva-core/src/index.js#L194-L201。而onReducer增强器本身是函数链式的在 packages/dva-core/src/Plugin.js#L89-L95 的getOnReducer中多个增强器按注册顺序依次包裹function getOnReducer(hook) { return function(reducer) { for (const reducerEnhancer of hook) { reducer reducerEnhancer(reducer); } return reducer; }; }这解释了示例中onReducer回调为什么是reducer (state, action) {...}的形态外层函数接收合并后的 reducer内层函数才是真正参与每次 dispatch 的 reducer。此外Plugin.js 中列出onReducer属于 dva 官方支持的 9 个 hook 之一onError、onStateChange、onAction、onHmr、onReducer、onEffect、extraReducers、extraEnhancers、_handleActions意味着它与 dva-loading 等插件的挂载机制完全兼容属于一等公民能力。模型与状态树present包装带来的结构变化示例的计数器模型定义在 src/models/counter.js非常标准export default { namespace: counter, state: 0, reducers: { add(state) { return state 1; }, minus(state) { return state - 1; }, }, };关键点在于由于onReducer用undoable包裹的是整棵合并后的 reducer 树而不是单个 namespace 的 reducer因此经过包装后Redux 的顶层 state 结构从{ router: ..., counter: 0 }变为{ past: [...], // 已发生的历史状态快照 present: { router: ..., counter: 0 }, // 当前状态 future: [...] // 被撤销后保留的未来快照 }这就是 src/index.js 中connect必须从present里取数据的原因const App connect(({ present: { counter } }) ({ counter, }))(props { ... });如果不经present解构而直接读取state.counter将得到undefined。这一点是集成redux-undo时最容易踩的坑务必牢记。视图层dispatch 与撤销/重做动作视图层同样是普通 React 组件通过connect拿到counter和dispatch。/-按钮照常派发 model 中定义的带命名空间的 actionprops.dispatch({ type: counter/minus }); props.dispatch({ type: counter/add });撤销与重做则依赖redux-undo导出的ActionCreatorsimport undoable, { ActionCreators } from redux-undo; props.dispatch(ActionCreators.undo()); // 回退一步 props.dispatch(ActionCreators.redo()); // 重放一步ActionCreators.undo()/ActionCreators.redo()返回对应的 action 对象由undoable高阶 reducer 内部拦截处理undo将present压入future、把past末尾弹出作为新的presentredo则反向操作。用户点击时dva 的dispatch与 redux 完全同构因此这些 action 会畅通无阻地到达被包裹的 reducer。从示例到实战集成要点与扩展方向基于本示例可以在实际 dva 项目中举一反三整树包装 vs 局部包装示例选择在onReducer中整体包裹 reducer 树实现全局可撤销。若只想让某个模块可撤销也可对单一 namespace 的 reducer 做undoable包装此时该 namespace 的状态会变成{ past, present, future }取数时需做相应调整。多增强器共存由于getOnReducer是链式包裹packages/dva-core/src/Plugin.js#L89-L95onReducer可以与 dva-loading 等其它 reducer 增强类插件同时配置顺序即包裹顺序。配置项留白示例传入{}使用 redux-undo 默认行为如需控制历史长度、按 action 过滤等可在该参数位传入对应配置如{ limit: 20 }但注意以所安装的redux-undo^0.6.1版本实际支持的选项为准。启动时序onReducer属于dva({...})初始化参数必须在app.start(#root)之前生效从 packages/dva/src/index.js#L51-L83 可见start内部才真正执行createStore因此初始化时的增强器配置会完整作用于最终 store。小结with-redux-undo示例展示了在 dva 中集成撤销/重做功能的最小完备路径通过onReducer配置项用undoable包裹合并后的 reducer 树src/index.js调整connect从present取数再用ActionCreators.undo()/ActionCreators.redo()驱动时间旅行。底层由 dva-core 的plugin.get(onReducer)与 Plugin.js 的链式增强机制保障这使得 redux-undo 与 dva 的模型体系、插件机制能够无缝协作。【免费下载链接】dva React and redux based, lightweight and elm-style framework. (Inspired by elm and choo)项目地址: https://gitcode.com/gh_mirrors/dv/dva创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

VitePress 部署完全指南:从本地构建、路径配置到主流平台一键上线

VitePress 部署完全指南:从本地构建、路径配置到主流平台一键上线

前端文档 【免费下载链接】vitepress Vite & Vue powered static site generator. 项目地址: https://gitcode.com/gh_mirrors/vi/vitepress 点击查看 免费下载 VitePress 是一个基于 Vite 与 Vue 的静态站点生成器,它把 Markdown 文档编译为纯静态…

2026/9/21 16:41:42 阅读更多 →
金融模型审计技能 audit-xls 深度解析:从公式级检查到三表勾稽完整性校验

金融模型审计技能 audit-xls 深度解析:从公式级检查到三表勾稽完整性校验

人工智能AI 应用AI 技能/插件AI Agent金融科技 【免费下载链接】financial-services 项目地址: https://gitcode.com/GitHub_Trending/fi/financial-services 点击查看 免费下载 在 Claude for Financial Services 开源仓库中,audit-xls 是贯穿投资银行…

2026/9/21 16:41:42 阅读更多 →
Relay 缓存复用完全指南:fetchPolicy、数据可用性与部分渲染实战

Relay 缓存复用完全指南:fetchPolicy、数据可用性与部分渲染实战

Relay 缓存复用完全指南:fetchPolicy、数据可用性与部分渲染实战 【免费下载链接】relay Relay is a JavaScript framework for building data-driven React applications. 项目地址: https://gitcode.com/gh_mirrors/relay29/relay Relay 在应用运行过程中会…

2026/9/21 16:41:42 阅读更多 →

最新新闻

Java与ABAP标记接口设计模式对比与实践

Java与ABAP标记接口设计模式对比与实践

1. 项目概述:当代码需要"暗号"时在面向对象编程的世界里,我们常常会遇到这样的场景:某些类需要被特殊对待,但又不想通过继承体系或显式接口来暴露这种特殊性。就像特种部队成员需要隐藏身份但内部又能快速识别一样&…

2026/9/21 17:10:50 阅读更多 →
C#与OpenClaw构建自动化商业闭环系统

C#与OpenClaw构建自动化商业闭环系统

1. 项目概述:C#与OpenClaw的自动化商业闭环在当今数字化浪潮中,一人公司(One Person Company,简称OPC)的运营模式正在经历革命性变革。传统需要多人协作完成的业务流程,现在通过智能自动化工具完全可以由单…

2026/9/21 17:10:50 阅读更多 →
DeepSeek Harness 首次启动引导演进:移除内测 Beta 通知的决策、遥测默认关闭与 settings.onboarding 缝合点设计

DeepSeek Harness 首次启动引导演进:移除内测 Beta 通知的决策、遥测默认关闭与 settings.onboarding 缝合点设计

人工智能AI AgentAgent 框架DeepSeek 【免费下载链接】deepseek-harness DeepSeek Harness: Everything is a Plugin. 项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness 点击查看 免费下载 本文依据 DeepSeek Harness 仓库中已归档的技术决策记录 20…

2026/9/21 17:09:49 阅读更多 →
Java 21 + Spring Boot 3 构建企业级 RAG 智能体工作流引擎

Java 21 + Spring Boot 3 构建企业级 RAG 智能体工作流引擎

1. 项目概述:为什么在 Java 生态里重做 RAG 智能体工作流不是“倒退”,而是精准卡位别卷 Python 了——这句话不是情绪宣泄,是我在连续交付 7 个 AI 增强型企业系统后的真实判断。过去两年,我带团队用 Python LangChain LangGr…

2026/9/21 17:08:48 阅读更多 →
system.img修改全攻略:格式识别、解包重打包与刷写避坑指南

system.img修改全攻略:格式识别、解包重打包与刷写避坑指南

1. system.img为何要改:先搞清楚这张"系统盘"的脾气玩Android的时间稍微长一点,基本都会遇到这个需求:自带应用删不掉、想加个开机脚本、想把某几个系统应用替换成自己改过的版本、或者单纯想把谷歌全家桶从国行固件里拔掉。这些操…

2026/9/21 17:08:47 阅读更多 →
变频与定频空调负荷聚合模型及Matlab实现

变频与定频空调负荷聚合模型及Matlab实现

1. 项目背景与核心价值在电力系统运行中,空调负荷占比逐年攀升,夏季高峰时段甚至可达总负荷的40%以上。传统控制方式往往将空调视为不可控负荷,导致电网调峰压力巨大。这项研究通过建立空调负荷的精细化控制模型,实现了三点突破&a…

2026/9/21 17:08:46 阅读更多 →

日新闻

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and …

2026/9/21 0:00:01 阅读更多 →
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,…

2026/9/21 0:00:01 阅读更多 →
Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

桌面应用AI 应用插件系统 【免费下载链接】Wox A cross-platform launcher that simply works 项目地址: https://gitcode.com/gh_mirrors/wo/Wox 点击查看 免费下载 全功能插件(Full-featured Plugin)是 Wox 三类插件实现方式中能力最完整的…

2026/9/21 0:00:01 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/21 3:13:20 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/21 2:19:36 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/21 4:51:05 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/21 15:36:51 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/21 15:36:51 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →