30 Seconds of Code:编写可读性更强的 Redux Reducer 实战指南
教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载本文围绕 30 Seconds of Code 仓库中 React 文章集合里的 redux-readable-reducers 一文展开系统讲解如何把日渐膨胀、难以维护的 Redux reducer 重构为类型常量集中、Action 结构统一、业务逻辑按单一职责拆分的可读代码。读完本文你将掌握一套可直接落地的 reducer 重构路线图并了解本仓库中与之配套的测试实践。说明本文示例基于 Redux其中描述的问题在 Redux 中更为常见。但由于这些问题并不局限于 Redux如果你正在为代码的复杂度、可读性和可测试性而头疼文中的思路与解法同样适用于其他状态管理方案。一、从一个典型的 Redux Reducer 说起在处理状态state时我们常常会遇到三类问题复杂度难以控制、代码可读性下降、测试难以编写。很多时候这些问题并不难解决——只要退一步找到问题的根源。先看一个典型的 Redux reducer 示例下文所有改进都围绕它展开请先理解它再继续阅读const initialState { id: null, name: , properties: {}, }; const generateID () Math.floor(Math.random() * 1000); const reducer (state initialState, action) { switch (action.type) { case createID: return { ...state, id: generateID(), }; case setName: return { ...state, name: action.name, }; case addProperty: return { ...state, properties: { ...state.properties, [action.propertyName]: action.propertyValue, }, }; case removeProperty: return { ...state, properties: Object.keys(state.properties).reduce((acc, key) { if (key ! action.propertyName) acc[key] state.properties[key]; return acc; }, {}), }; default: return state; } };这个 reducer 管理着一个简单实体一个可生成的id、一个可修改的name以及一组可增删的properties。当前代码量不大但已经隐约露出了几处隐患。二、识别问题复杂度、心智负担与硬编码对上面的示例进行分析可以发现三类典型的坏味道1. 每个 action 的逻辑都嵌套在 reducer 内复杂度随 action 数量线性膨胀。当前代码并不算复杂但随着应用需要处理更多 action typeswitch分支会越来越多reducer函数体持续膨胀可读性快速恶化。2. 每个 action 的结构不一致增加维护者的认知负担。示例中setName依赖action.name而addProperty/removeProperty依赖action.propertyName与action.propertyValue。后续维护者必须记住每个 action 各自携带哪些键心智负担很大而且不同 action 键名散落各处也容易拼错。另一个潜在隐患是当action.type本身也需要作为数据写入 state例如 state 里需要存一个type字段时type这个命名空间被占用会带来命名冲突的困扰。3.action.type的值被硬编码在 reducer 内部。createID、setName这类字符串散落在 reducer 里在其它文件、组件中派发 action 时难以记忆、难以同步改一处漏一处是常见事故。这看起来是最小的问题却也是最容易修复的因此我们先从它入手。三、第一步提取 Action Types 常量解决硬编码字符串最直接的方式是把所有action.type取值提取到一个集中定义的对象中。这样既能消除拼写错误又能让 dispatch 端与 reducer 端共享同一份常量定义const ACTION_TYPES { CREATE_ID: createID, SET_NAME: setName, ADD_PROPERTY: addProperty, REMOVE_PROPERTY: removeProperty };把常量集中到一处之后reducer 与组件中的 dispatch 都引用ACTION_TYPES.XXX类型值的变化只需修改一处。这也是本仓库中 redux-readable-reducers 一文首推的改动成本最低、收益立竿见影。四、第二步统一 Action 结构payload 约定示例中各个 action 对象除了共享type键外其余结构五花八门。为了降低记忆负担、减少踩坑应统一所有 action 的结构——把整个 action 的数据负载放到顶层payload键下所有传给 action 的值都嵌套其中// 传入 reducer 函数的任意 action 的统一结构 const action { // 任意一个已定义的 action type type: ACTION_TYPES.CREATE_ID, // 将 name、propertyValue、propertyKey 等全部嵌套进该对象 payload: { /* ... */ } }例如setName的 payload 为{ name }addProperty的 payload 为{ propertyName, propertyValue }。刚接入这种结构时可能会觉得不直观多了一层嵌套但请先耐心看下去——它正是下一步“抽取嵌套逻辑”能顺利实施的前提。这种{ type, payload }的约定与 Redux 社区常见的 Flux Standard ActionFSA规范思路一致所有 action 对外只暴露type与payload两个顶层键状态更新的数据来源变得可预期。五、第三步抽取嵌套逻辑为单一职责函数前面两步改造最终服务于最关键的一步——把原先嵌套在switch分支中的逻辑抽成各自独立的纯函数。每个case对应一个函数函数只负责自己那一个 action type 的状态更新const createID state ({ ...state, id: generateID(), }); const setName (state, { name }) ({ ...state, name, }); const addProperty (state, { propertyName, propertyValue }) ({ ...state, properties: { ...state.properties, [propertyName]: propertyValue, }, }); const removeProperty (state, { propertyName }) { const properties Object.keys(state.properties).reduce((acc, key) { if (key ! propertyName) acc[key] state.properties[key]; return acc; }, {}); return { ...state, properties }; };注意setName、addProperty、removeProperty都直接对payload进行解构取值如{ name }、{ propertyName, propertyValue }这正是第四节统一payload结构带来的直接红利函数签名整齐划一全部是(state, payload)。每个函数只承担一个职责与某个 action type 相关的全部复杂度都被收拢进对应的小函数中。测试这些小型纯函数也远比测试一个臃肿的 reducer 容易——它们聚焦于单一任务没有嵌套在庞大的switch里输入输出清晰、可独立验证。六、整合重构后的完整代码将上述三步改动合并最终代码如下const initialState { id: null, name: , properties: {}, }; const ACTION_TYPES { CREATE_ID: createID, SET_NAME: setName, ADD_PROPERTY: addProperty, REMOVE_PROPERTY: removeProperty }; const generateID () Math.floor(Math.random() * 1000); const createID state ({ ...state, id: generateID(), }); const setName (state, { name }) ({ ...state, name, }); const addProperty (state, { propertyName, propertyValue }) ({ ...state, properties: { ...state.properties, [propertyName]: propertyValue, }, }); const removeProperty (state, { propertyName }) { const properties Object.keys(state.properties).reduce((acc, key) { if (key ! propertyName) acc[key] state.properties[key]; return acc; }, {}); return { ...state, properties }; }; const reducer (state initialState, action) { switch (action.type) { case ACTION_TYPES.CREATE_ID: return createID(state, action.payload); case ACTION_TYPES.SET_NAME: return setName(state, action.payload); case ACTION_TYPES.ADD_PROPERTY: return addProperty(state, action.payload); case ACTION_TYPES.REMOVE_PROPERTY: return removeProperty(state, action.payload); default: return state; } };重构后的reducer变成了一个纯粹的分发器dispatcher只负责把action.type映射到对应的处理函数不再包含任何具体业务逻辑具体逻辑全部下沉到createID、setName、addProperty、removeProperty四个单一职责函数中各自的复杂度被有效隔离。小提示保持命名一致。原文档最终示例中常量引用写作TYPES.xxx、createId(state, ...)与前面定义的ACTION_TYPES和createID存在命名不一致。上述版本统一为ACTION_TYPES与createID读者在实际项目中也应确保常量名、函数名的大小写风格全局一致避免同样的困惑。七、仓库内的配套实践与延伸阅读在 30 Seconds of Code 仓库中这篇 reducer 文章归属于 React 文章集合见 content/collections/react/index.yaml并且与它配套的还有一篇 Redux 主题的测试文章。1. 让拆分后的 reducer 更好测。拆分出单一职责函数后每个状态更新函数都可以作为纯函数单独断言。本仓库的 testing-redux-connected-components 一文展示了如何在组件层配合 Redux store 测试——它通过createStore(reducer, initialState)创建测试 store再用 React Testing Library 的render与Provider包裹被测组件从而在真实 reducer 参与的情况下验证 UI 行为。如果你的 reducer 已经在本文的路线下被拆分成小函数那么组件测试中传入的initialState与 dispatch 后的状态断言都会更加直观。2. 了解文章的来源与路由。这篇 reducer 文章最早发布在/articles/s/react-redux-readable-reducers路径下仓库通过 content/redirects.yaml 将其 301 重定向到当前的 redux-readable-reducers保证历史链接依然有效。3. 参考文章的元数据格式。该文章使用仓库统一的 snippet 前置元数据格式参考 content/snippets/react/snippet-template.md包括title、shortTitle、language: react、tags: [logic]、excerpt、listed与dateModified等字段方便搜索引擎与站点列表如 React 集合中的 testing.yaml自动聚合与检索。总结Redux reducer 的复杂度问题不是靠“更小心地写”就能避免的而应通过结构性改进根治提取ACTION_TYPES常量消灭硬编码字符串统一{ type, payload }的 action 结构降低维护者的记忆负担将每个 case 抽取为单一职责函数让 reducer 退化为纯分发器业务逻辑各归其位。三步改造之后reducer 的可读性、可维护性与可测试性都会显著提升配合本仓库中关于 Redux 组件测试的配套实践可以形成一套完整的状态管理开发与验证闭环。赞分享教程文档【免费下载链接】30-seconds-of-codeCoding articles to level up your development skills项目地址https://gitcode.com/gh_mirrors/30/30-seconds-of-code点击查看免费下载相关推荐30-seconds-of-code 实战编写一份专业可用的 CSS 打印样式表Print Stylesheet30 seconds of code 实战编写一份专业可用的 CSS 打印样式表Print Stylesheet 打印网页内容虽不常见但 打印样式表p教程文档30 Seconds of Code 实战指南写出高质量 Pull Request 的 5 个技巧30 Seconds of Code 实战指南写出高质量 Pull Request 的 5 个技巧 写出一手好代码只是工作的一半。本文基于 30 Second教程文档30 seconds of code 文章编写规范读懂 snippet-template 内容模板与 Frontmatter30 seconds of code 文章编写规范读懂 snippet template 内容模板与 Frontmatter 30 seconds of co教程文档上一篇终极Winlator性能优化指南如何让Android手机流畅运行Windows游戏下一篇MOOTDX实战宝典5个痛点场景的终极解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

OWASP MASTG 最佳实践:Android 内部 IPC 必须使用显式 Intent(MASTG-BEST-0056)

OWASP MASTG 最佳实践:Android 内部 IPC 必须使用显式 Intent(MASTG-BEST-0056)

文档教程网络安全 【免费下载链接】mastg The OWASP Mobile Application Security Testing Guide (MASTG) is a comprehensive manual for mobile app security testing and reverse engineering. It describes technical processes for verifying the OWASP Mobile Security W…

2026/10/5 10:14:51 阅读更多 →
[RAG在LangChain中的实现-01]让LLM在指定的上下文范围内回答问题

[RAG在LangChain中的实现-01]让LLM在指定的上下文范围内回答问题

LLM具有两大的局限:一是它不知道自己不知道,所以我们发现它经常天马行空、一本正经地胡说八道,这就是所谓的幻觉;二是模型具有的知识在训练生成的那一刻就已经冻结,知识体系不会继续更替。RAG是目前针对这两个问题的主…

2026/10/5 10:14:51 阅读更多 →
5款AI写论文哪个好?一个正在被“合规拷问”改变的标准

5款AI写论文哪个好?一个正在被“合规拷问”改变的标准

云智变AI官网www.yunzhibian.cn 微信公众号搜一搜 云智变ai学术 “5款AI写论文哪个好”——这个问题在2026年有了一个新答案。 不是因为工具本身变了,而是因为评价标准变了。2026年,中国学位与研究生教育学会受教育部研究生司委托,正式发布…

2026/10/5 10:14:50 阅读更多 →

最新新闻

Kimi K2 驱动 AI 文档阅读助手实战:零代码用 Claude Code 一天打造全栈文档管理网站

Kimi K2 驱动 AI 文档阅读助手实战:零代码用 Claude Code 一天打造全栈文档管理网站

文档教程知识库人工智能 【免费下载链接】ai-guide 程序员鱼皮的 AI 资源大全 Vibe Coding 零基础教程,分享 OpenClaw 保姆级教程、大模型玩法(DeepSeek / GPT / Gemini / Claude / GLM)、最新 AI 资讯、Prompt 提示词大全、AI 知识百科&…

2026/10/5 14:21:41 阅读更多 →
SAP物料账报错ML4HMASTER113与ML4HRUN053根因解析

SAP物料账报错ML4HMASTER113与ML4HRUN053根因解析

1. 项目概述:这不是一次简单的报错修复,而是一次对SAP物料账(Material Ledger)底层逻辑的深度体检“SAP-ML章<<<<第一节:物料账报错处理>>&#x…

2026/10/5 14:21:40 阅读更多 →
本科毕设遥感图像分类实战:72小时落地深度学习方案

本科毕设遥感图像分类实战:72小时落地深度学习方案

1. 这不是“速成课”,而是毕设场景下真正能落地的遥感图像分类实战路径 我带过三届毕业设计,每年四月总有一批学生抱着“毕设有救了”的心态冲进实验室,手里攥着刚下载的Sentinel-2数据、GitHub上抄来的PyTorch代码、还有导师一句“你试试用深…

2026/10/5 14:21:40 阅读更多 →
C++ STL:list 容器详解与模拟实现——从双向链表到反向迭代器

C++ STL:list 容器详解与模拟实现——从双向链表到反向迭代器

C STL:list 容器详解与模拟实现——从双向链表到反向迭代器 文章目录C STL:list 容器详解与模拟实现——从双向链表到反向迭代器1 list 的基本概念2 list 的构造2.1 构造空 list2.2 构造 n 个相同元素2.3 拷贝构造2.4 使用迭代器区间构造3 list 的迭代器…

2026/10/5 14:21:40 阅读更多 →
深入理解Spring Data:从JDBC样板代码到Repository自动化原理

深入理解Spring Data:从JDBC样板代码到Repository自动化原理

过去几年里,我带过不少刚入行的Java开发,大多数人第一次听到“Spring Data”这个词时,第一反应都是:这是个ORM框架吧?是不是跟MyBatis差不多?等真正接手项目,看到Service层里一个个接口注入、方…

2026/10/5 14:20:39 阅读更多 →
PHP短视频源码开发:JSON数据源统一接入与API适配层设计实践

PHP短视频源码开发:JSON数据源统一接入与API适配层设计实践

在做PHP开源短视频源码的时候,我遇到的第一件事不是播放器怎么接,也不是会员体系怎么做,而是第三方数据源的JSON格式乱到让人怀疑人生。短剧接口返回的字段和TVBox仓库对不上,TVBox仓库的结构和zyplayer视频源又不是一回事&#x…

2026/10/5 14:20:39 阅读更多 →

日新闻

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

马斯克杀回智能体战场,Grok 4.5万亿参数撑腰,Cursor接手数字白领项目:用TaoToken统一Key跑通多模型Agent工作流

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:22 阅读更多 →
AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

AI编程工具插件机制详解:plugin.json配置与加载失败排查指南

1. 从“plugins”这个词说起:它到底在解决什么问题如果你最近在折腾 AI 编程工具,尤其是 Cursor、Codex CLI、Claude Code 这类带 CLI 的编辑器或命令行助手,那你大概率绕不开一个词——plugins。这个词本身不新鲜,从浏览器到 IDE…

2026/10/5 0:00:23 阅读更多 →
第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

第26课:OpenClaw|日志审计与问题诊断:把日志链路改到 TaoToken 的排查清单

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 0:00:23 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 5:06:42 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 1:10:22 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/5 3:06:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 11:40:45 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 9:43:54 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/4 20:14:29 阅读更多 →