新闻详情

新闻详情

首页 / 资讯中心 / 详情

Rematch @rematch/loading 插件详解:自动化的 Effects 加载状态管理

发布时间:2026/9/25 3:05:12来源:尧图网络
Rematch @rematch/loading 插件详解:自动化的 Effects 加载状态管理
前端【免费下载链接】rematchThe Redux Framework项目地址https://gitcode.com/gh_mirrors/re/rematch点击查看免费下载在 Rematch 应用中effects 往往对应着异步请求拉取数据、提交表单等而加载中 / 成功 / 失败这类 UI 状态如果全靠手动维护loading: true不仅繁琐还容易在 effect 提前抛错时漏掉复位。rematch/loading插件正是为此而生它会自动为 effects 生成三层粒度的加载状态全局、按 model、按具体 effect你只需要在 store 中挂载插件即可通过useSelector直接消费状态。本文基于官方文档 docs/plugins/loading.md 展开并结合 packages/loading/src/index.ts 的源码实现与测试用例讲清它的配置项、状态结构、TypeScript 类型接入方式以及底层计数器 effect 包装的工作原理。插件定位与版本兼容性rematch/loading的官方定位是为 Rematch 添加自动化的 loading 指示器让你无需自己维护loading: true之类的状态其设计灵感来源于 dva-loading见 packages/loading/package.json 中的 description 字段与 docs/plugins/loading.md。安装时需注意插件版本必须与 core 版本匹配rematch/corerematch/loading1.x.x1.x.x2.x.x2.x.xrematch/loading的 package.json 中声明了peerDependencies: { rematch/core: 2 }即 2.x 的 loading 插件要求 core 至少为 2.x。当前仓库中该包版本为 2.1.2。安装npm install rematch/loadingloadingPlugin([config]) 配置项详解插件接收一个可选的config对象全部配置项在源码 LoadingConfig 接口 中定义export type LoadingPluginType number | boolean | full export interface LoadingConfig { name?: string whitelist?: string[] blacklist?: string[] type?: LoadingPluginType /** * deprecated Use type: number instead */ asNumber?: boolean }各参数说明name(string?)loading 模型在 store 中的键名。命名为custom后加载状态就从state.custom读取。默认值为loading。asNumber(boolean?)默认情况下插件用布尔值跟踪运行中的 effects如state.loading.global true设为true后改为记录 effect 被执行了多少次如state.loading.global 5。已废弃请改用type: number。type(number | boolean | full)boolean默认只表示是否有 effect 正在运行number记录每个 effect 被调用的次数适合并发去重或展示进行中数量full同时跟踪 effect 是否 loading、是否以 Error 结束、是否正确 resolve即{ loading, success, error }三元组。whitelist(string[]?)白名单。指定后插件只为列表中的 effects 工作。blacklist(string[]?)黑名单。指定后插件对所有 effects 工作但排除列表中的。whitelist与blacklist都接受modelName/effectFunctionName格式的完整 effect 名例如count/addOne。两者都未提供时插件对所有 effects 生效。从源码的 validateConfig 可以看到这些约束在开发环境下会被强制校验NODE_ENV ! production时name必须是字符串否则抛错asNumber传入时控制台会打印 deprecation warning提示替换为type: numberwhitelist/blacklist必须是字符串数组两者不能同时提供同时传入会直接抛错loading plugin config cannot have both a whitelist a blacklist。上述每条约束在 packages/loading/test/loading-asBoolean.test.ts 中都有对应的测试用例覆盖如should throw if contains both a whitelist blacklist。Loading 状态结构以 store 中有一个count模型为例默认boolean模式下插件注入的状态结构为{ global: true, // true when any effect in any model is loading models: { count: true // true when any effect in count model is loading }, effects: { count: { addOne: true // true when effect addOne in model count is loading } } }三层粒度的含义loading.global任意模型的任意 effect 正在运行即为trueloading.models.countcount模型内任一 effect 正在运行即为trueloading.effects.count.addOne精确到count 模型下的 addOne effect是否正在运行。这套结构在源码的类型定义 LoadingState 接口 中可以直接对应其中effects一层甚至基于ExtractRematchDispatchersFromEffects自动推导每个模型的具体 effect 名——这意味着只要 store 配置正确rootState.loading.effects.count.addOne这样的访问在 TypeScript 中是完整的写错 effect 名会直接报类型错误。将插件接入 StoreTypeScript假设已有如下 model来自官方文档的示例docs/plugins/loading.md 的countModel// filename: count.ts import { createModel } from rematch/core import { RootModel } from ./models export const count createModelRootModel()({ state: 0, reducers: { increment(state, payload: number) { return state payload }, }, effects: (dispatch) ({ async incrementAsync(payload: number, state) { dispatch.count.increment(payload) }, }), })// filename: models.ts import { Models } from rematch/core import { count } from ./count export interface RootModel extends ModelsRootModel { count: typeof count } export const models: RootModel { count }默认boolean 模式// filename: store.ts import loadingPlugin, { ExtraModelsFromLoading } from rematch/loading import { init, RematchDispatch, RematchRootState } from rematch/core import { models, RootModel } from ./models type FullModel ExtraModelsFromLoadingRootModel export const store initRootModel, FullModel({ models, plugins: [loadingPlugin()], }) export type Store typeof store export type Dispatch RematchDispatchRootModel export type RootState RematchRootStateRootModel, FullModel关键点init的第二个泛型参数FullModel声明了插件额外注入的模型而ExtraModelsFromLoadingRootModel正是 packages/loading/src/index.ts 导出的类型它会在原有RootModel的基础上追加一个loading模型声明使RematchRootState能推导出state.loading的完整形状export interface ExtraModelsFromLoading TModels extends ModelsTModels, TConfig extends LoadingConfig { type: boolean } extends ModelsTModels { loading: LoadingModelTModels, ... }注意它的第二个泛型参数默认就是{ type: boolean }所以不传任何配置时类型系统默认按布尔模式推导。number 模式// filename: storeAsNumber.ts import loadingPlugin, { ExtraModelsFromLoading } from rematch/loading import { init, RematchDispatch, RematchRootState } from rematch/core import { models, RootModel } from ./models type FullModel ExtraModelsFromLoadingRootModel, { type: number } export const store initRootModel, FullModel({ models, plugins: [loadingPlugin({ type: number })], })此时state.loading.global、state.loading.models.count等变为数字被调用的次数类型上由 PickLoadingPluginType 映射number→number、full→DetailedPayload、其余 →boolean。full 模式// filename: storeAsFull.ts import loadingPlugin, { ExtraModelsFromLoading } from rematch/loading import { init, RematchDispatch, RematchRootState } from rematch/core import { models, RootModel } from ./models type FullModel ExtraModelsFromLoadingRootModel, { type: full } export const store initRootModel, FullModel({ models, plugins: [loadingPlugin({ type: full })], })full模式下每个加载点都是{ loading, success, error }三元组类型定义见 DetailedPayloadtype DetailedPayload { error: unknown success: boolean loading?: boolean }packages/loading/test/loading-asFull.test.ts 验证了完整状态流转effect 开始时为{ loading: true, success: false, error: false }resolve 后变为{ loading: false, success: true, error: false }若 effect 抛出异常则error字段直接存放该 Error 对象测试中用 inline snapshot 断言error: [Error: effect error]。React 中使用下面分三种模式给出消费方式。默认booleanimport React from react import { useSelector } from react-redux import { RootState } from ./store export const App () { const isCountLoading useSelector( (rootState: RootState) rootState.loading.models.count ) if (isCountLoading) return divLOADING.../div return divData succesfully loaded/div }full 模式import React from react import { useSelector } from react-redux import { RootState } from ./storeAsFull export const App () { const { loading, success, error } useSelector( (rootState: RootState) rootState.loading.models.count ) if (loading) return divLOADING.../div if (error) return div{(error as Error).name}/div return divData succesfully loaded/div }full 模式让失败原因也成为可消费状态——上例直接把error.name渲染出来无需在业务层额外捕获。number 模式import React from react import { useSelector } from react-redux import { RootState } from ./storeAsNumber export const App () { const countCalledTimes useSelector( (rootState: RootState) rootState.loading.models.count ) if (countCalledTimes 0) return divLOADING.../div return divData succesfully loaded/div }源码深潜插件是如何实现的理解 packages/loading/src/index.ts 的实现后很多行为细节比如计数器为何不会漂移、同步 effect 为何不会报错都能找到答案。1. 内部永远用计数器输出时再翻译插件内部维护了一个cntStateInitialStatenumber类型即纯数字结构而真正暴露给用户的 state 由一个converter函数翻译而成源码 L195-L204const converter: ConverterLoadingPluginType (cnt, detailedPayload) { if (isAsNumber) return cnt if (isAsDetailed detailedPayload) { return { ...detailedPayload, loading: cnt 0 } as DetailedPayload } if (isAsDetailed) { return { loading: cnt 0, success: false, error: false } } return cnt 0 }这就是三种输出模式共用同一套计数逻辑的原因boolean模式判断cnt 0number模式原样返回cntfull模式把计数是否大于 0 与成功/失败信息合并为DetailedPayload。2. show / hide 两个 reducer 驱动状态loading 模型只有两个 reducer——show与hide它们由 createLoadingAction 工厂函数生成参数i分别为1和-1const loading: LoadingModelTModels, LoadingPluginType { name: loadingModelName, reducers: { hide: createLoadingAction(converter, -1, cntState), show: createLoadingAction(converter, 1, cntState), }, state: loadingInitialState, }createLoadingAction每次被调用时会同步修改cntState中global、models[name]、effects[name][action]三处计数然后用converter把新计数翻译成对外 state 的三个对应位置。由于内部计数 对外转换是分离的即使boolean模式下多个 effect 并发测试用例should capture all model and global loading for simultaneous effects验证了这一点两个 effect 同时跑时计数为 2第一个结束后计数回落为 1models.count仍保持true直到全部结束才变为false状态也绝不会错误地提前翻转。3. onModel 钩子包装每个 effect插件返回对象的onModel回调在 core 注册每个模型时被触发核心逻辑源码 L234-L338分四步跳过自身模型名等于loadingModelName默认loading时直接 return避免无限递归同时为cntState和初始状态预留该模型的计数槽位。识别 effect遍历rematch.dispatch[name]的每个 action依赖 core 为 dispatcher 打上的isEffect标记packages/core/src/dispatcher.ts 注释说明该属性专门用于区分 effect 与 reducer 的 dispatcher只对 effect 做处理。黑白名单过滤拼出modelName/actionName形式的 actionType若不在whitelist中或在blacklist中则跳过包装——这就是为什么白/黑名单要写成count/addOne这种完整格式。包装并替换保存原始 effect 引用生成effectWrapper后写回rematch.dispatch[name][action] effectWrapper并保留effectWrapper.isEffect true标记确保后续依赖isEffect的插件/类型推导不受影响。4. effectWrappershow → 执行 → hide 的生命周期包装器的行为可以概括为一个 try 包裹的三段式流程dispatch loading/show计数 1 ↓ 执行原 effect ↓ 若返回值是 Promise resolve → dispatch loading/hide计数 -1successtrue后 return 结果 reject → dispatch loading/hide计数 -1errorerr后重新 throw 否则同步返回 立即 dispatch loading/hide 并 return 结果 catchhide 后重新 throw几个值得注意的细节错误不吞没无论 effect 内部 reject、还是抛出非 Promise 异常包装器都会在hide之后throw err重新抛出。测试 should allow the propagation of the error 与should handle hide if effect throws分别验证了错误照常向外传播与异常路径下 loading 也会被正确复位。结果原样透传should allow the propagation of the effect result测试确认包装后的 effect 返回值如foo不会被插件篡改。完整的 action 序列可观察should trigger four actions测试通过 redux middleware 记录了完整序列——loading/show→count/timeout→count/addOne→loading/hide即一次 effect 调用最少产生show 一次、hide 一次两个 action。这个序列在 Redux DevTools 中直接可见也是排查loading 卡住问题的抓手show 与 hide 计数不配对即可定位。5. 自定义 name 与 deprecation 处理const loadingModelName config.name || loading决定了状态键名与 action 前缀自定义为foobar后即为foobar/show、foobar/hide。而asNumber: true会在运行时被映射为config.type number源码 L189-L191并触发控制台 deprecation 警告——这是官方明确给出的迁移路径。实战示例examples/loading-react仓库中的 examples/loading-react 演示了一个典型的异步提交场景。模型定义src/models.js中submiteffect 模拟了 3 秒的网络延迟export const count { state: 0, reducers: { addOne(s) { return s 1 }, }, effects: { async submit() { // mocking the delay of an effect await asyncDelay(3000) this.addOne() }, }, }store 初始化src/index.js使用 number 模式const loadingPlugin createLoadingPlugin({ asNumber: true }) const store init({ models, plugins: [loadingPlugin], })视图层src/App.js用connect同时映射三层状态直观展示了粒度选择const mapState (state) ({ count: state.count, loading: { global: state.loading.global, model: state.loading.models.count, effect: state.loading.effects.count.submit, }, })页面中三个Loading组件分别绑定loading.global、loading.models.count、loading.effects.count.submit点击 Submit Async 后 3 秒内三个指示器同时出现submitresolve 后同时消失。该示例基于旧版connectAPI 编写asNumber: true写法新项目建议按前文 TypeScript 章节的type: numberuseSelector方式接入。小结rematch/loading把 effects 的加载状态变成了声明即可得的 store 状态粒度分为global/models.*/effects.*三层通过type在boolean是否加载中、number调用次数、full{ loading, success, error }三种语义间切换类型系统通过ExtraModelsFromLoadingRootModel, { type: xxx }同步感知whitelist/blacklist以modelName/effectName精确控制作用范围二者互斥从源码看插件靠内部纯计数器 converter 翻译 effect 包装show/hide 配对实现异常路径保证 hide 必被调用且错误照常向外传播行为均有 packages/loading/test 下的测试用例兜底。赞分享前端【免费下载链接】rematchThe Redux Framework项目地址https://gitcode.com/gh_mirrors/re/rematch点击查看免费下载相关推荐7个实用技巧Rematch状态管理库的完整测试指南7个实用技巧Rematch状态管理库的完整测试指南 Rematch是基于Redux的状态管理框架它简化了Redux的使用流程让状态管理变得更加直观和高效。前端微信聊天记录的数字永生WeChatMsg如何让你的对话记忆永不褪色微信聊天记录的数字永生WeChatMsg如何让你的对话记忆永不褪色 在数字时代我们每天通过微信交换的信息量相当于一本中等篇幅的书籍然而这些珍贵的对话记忆往Rematch插件生态终极指南Immer、Loading与Persist的实战应用Rematch插件生态终极指南Immer、Loading与Persist的实战应用 Rematch是一个基于Redux的轻量级状态管理框架它通过减少样板代码前端上一篇Excalidraw 手绘白板三步跑起来画图和协作一次搞定下一篇AIOS快速上手指南5分钟搭建个人AI代理操作系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

三年实测精选:10个Chrome扩展提升效率与开发体验 2026/9/25 3:46:29

三年实测精选:10个Chrome扩展提升效率与开发体验

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

阅读更多 →
Pyro 分布测试工具指南:Goodness-of-Fit 拟合优度检验(gof 模块深度解析) 2026/9/25 3:46:22

Pyro 分布测试工具指南:Goodness-of-Fit 拟合优度检验(gof 模块深度解析)

人工智能机器学习深度学习概率编程 【免费下载链接】pyro Deep universal probabilistic programming with Python and PyTorch 项目地址: https://gitcode.com/gh_mirrors/py/pyro 点击查看 免费下载 本篇技术指南围绕 Pyro 官方文档中的 Testing Utilities 一节&…

阅读更多 →
RocketRide background_removal 节点实战指南:基于 BiRefNet 的图像背景移除与 RGBA 抠图 2026/9/25 3:46:16

RocketRide background_removal 节点实战指南:基于 BiRefNet 的图像背景移除与 RGBA 抠图

【免费下载链接】rocketride-server High-performance AI pipeline engine with a C core and 50 Python-extensible nodes. Build, debug, and scale LLM workflows with 13 model providers, 8 vector databases, and agent orchestration, all from your IDE. Includes VS C…

阅读更多 →
基于Python的多模态垃圾分类系统:双塔融合与工程实现 2026/9/25 3:46:10

基于Python的多模态垃圾分类系统:双塔融合与工程实现

简介:基于Python实现的多模态垃圾分类系统,是一套面向高校课程设计及毕业设计的完整工程资源,适合需要完成垃圾分类相关项目的计算机、环境类专业学生。系统融合图像与文本两种模态信息,支持上传垃圾图片或输入名称描述&#xff0…

阅读更多 →
开源AI编程工具指南:从选型到本地部署与提示词实战 2026/9/25 3:46:10

开源AI编程工具指南:从选型到本地部署与提示词实战

1. 为什么认真对待开源AI编程工具过去大半年时间,我几乎每天都会打开AI编程工具写代码。坦白说,C、Copilot、Windsurf、Trae这些商业产品确实做得很好,很多功能的完成度远超开源项目。但我越用越觉得,值得单独拿出来聊聊的是开源工…

阅读更多 →
24GB显卡跑70B大模型:GPTQ/AWQ/GGUF量化踩坑与TaoToken配置实录 2026/9/25 3:46:04

24GB显卡跑70B大模型:GPTQ/AWQ/GGUF量化踩坑与TaoToken配置实录

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

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉