安德烈·冈察洛夫
另一个减少 Redux(NGRX)应用中样板文件的指南
这里我们要讲什么?
在本文中,我们将讨论几种方法/技巧/窍门/古老的黑魔法仪式,以减少我们充斥着样板代码的 Redux(和 NGRX!)应用中的样板代码。这些是我多年来从第一手生产经验中得出的。
让我坦诚地告诉大家。起初,我只想谈谈我的新微库 flux-action-class 。但最近,技术博客似乎越来越像 Twitter……也许你想读一些更有意义的长篇文章。所以我想:“管他呢?我有一些自己的经验和最佳实践,这是我付出的汗水和心血。也许,它可以帮助一些人。也许,人们可以帮助我改进其中的一些。”
识别样板
让我们看一个如何在 Redux 中发出 AJAX 请求的典型示例。在这个特定情况下,假设我们想从服务器获取猫列表。
如果你想知道为什么我有选择器工厂(makeSelector…),请看 这里我故意省略了副作用处理。这是另一篇文章的主题,充满了青少年对现有生态系统的愤怒和批评 :D
该代码有几个弱点:
- 动作创建者本身就是唯一对象,但我们仍然需要动作类型来实现序列化。我们可以做得更好吗?
-
当我们添加实体时,我们不断重复翻转标志的相同逻辑
loading
。实际的服务器数据和我们想要处理它的方式可能会改变,但逻辑loading
始终相同。我们可以摆脱它吗? - Switch 语句是 O(n)(这本身不是一个可靠的论点,因为 Redux 的性能本来就不高)。Redux 需要为每种情况添加几行额外的代码,并且 switch 不能轻易组合。我们能找到更高效、更易读的方法吗?
- 我们真的需要为每个实体分别保留一个错误吗?
- 使用选择器是个好主意。这样,我们就有了商店的抽象,只需调整选择器就可以改变其形状,而不会破坏整个应用程序。然而,由于记忆化的工作方式,我们必须为每个选择器创建一个工厂。还有其他方法吗?
提示 1:摆脱动作类型
其实不然。但我们可以让 JS 帮我们生成它们!
让我们花点时间思考一下为什么我们甚至需要动作类型。当然,这是为了帮助 Reducer 以某种方式区分传入的动作并相应地更改我们的状态。但它真的必须是字符串吗?如果我们只有一种方法来创建某些类型的对象(动作)……类来救援!我们绝对可以使用类作为动作创建者并按类型执行 switch
。像这样:
一切都很好,但有一件事……我们无法再序列化和反序列化我们的操作。它们不再是具有 Object 原型的简单对象。它们都具有唯一原型,这实际上使切换 action.constructor
工作正常。天哪,我喜欢将我的操作序列化为字符串并将其附加到错误报告中的想法。那么我们能做得更好吗?
事实上,是的!幸运的是,每个类都有一个名称,它是一个字符串,我们可以利用它们。因此,为了序列化的目的,每个操作都需要是一个带有字段的简单对象 type
(请查看 此处 以了解任何自尊操作还应具有什么)。我们可以 type
向每个使用类名的类添加 getter。
它可以工作,但这样我们就不能像 这个 很棒的提案所建议的那样为我们的行动类型添加前缀(实际上,我更喜欢它的 后继者 )。要解决前缀问题,我们应该停止直接使用类的名称,并为其创建另一个 getter。这次是一个静态的 getter。
让我们稍微完善一下,以避免代码重复,并添加一个假设,以进一步减少样板代码。如果操作是错误的,则操作 payload
必须是 Error
.
此时,它与 NGRX 完美配合。Redux 抱怨调度非纯对象(它验证了原型链)。幸运的是,JS 允许我们从构造函数返回任意值,并且我们实际上不需要我们的操作具有原型。
为了不让你们复制粘贴 ActionStandard
类并担心它的可靠性,我创建了一个 名为 flux-action-class 的小型库 ,其中所有代码都已通过测试,代码覆盖率为 100%,用 TypeScript 编写,适用于 TypeScript 和 JavaScript 项目。
技巧 2:组合你的 Reducer
这个想法很简单:不仅将 CombineReducers 用于顶级 Reducer,还用于组合 Reducer loading
和其他东西。让代码说明一切:
建议 3:远离开关
使用对象并按键从中挑选!按键挑选对象的属性是 O(1),如果你问我,它看起来更干净。像这样:
稍微 reducerLoading
重构 reducerLoading
。我们可以根据需要扩展它(与 Switch 不同)。
createReducer
. Do not hesitate to use it.
技巧 4:拥有全局错误处理程序
没有必要为每个实体保留一个错误。在大多数情况下,我们需要显示一个错误对话框或其他内容。所有实体都显示相同的错误对话框!
创建一个全局错误处理程序。在最简单的情况下,它可能看起来像这样:
然后在副作用的 catch
块调度 ErrorInit
它可能看起来像这样 redux-thunk :
猫的部分状态 error
提供减速器 CatsGetError
只需翻转 loading
标志。
建议 5:不要再记忆一切
让我们再看看选择器带来的混乱。 I omitted makeSelectorCatsError
because of what we discovered in the previous section.
为什么我们要为所有内容创建记忆选择器?有什么需要记忆的?通过键选择对象的字段(这正是这里发生的事情)是 O(1)。只需编写一个常规的非记忆函数。仅当您想要以需要非常量时间的方式更改存储中数据的形状并将其返回到组件之前时,才使用记忆。
只有在计算一些派生数据时,记忆化才有意义。在这个例子中,我们假设每只猫都是一个带有字段的对象 name
,我们需要一个包含所有猫的名字的字符串。
结论
让我们回顾一下我们从哪里开始:
结果是:
希望您能找到对您的项目有用的东西。请随时向我传达您的反馈!我非常欢迎任何批评和问题。
发表评论 取消回复