使用Redux的困难及其解决方案

图片

我们中间的救赎


这是最受欢迎的状态管理器之一

它易于使用,透明且可预测。 使用它,您可以组织数据的存储和修改。 而且,如果我们假设动作减速器redux`a的一部分,那么我们可以毫不夸张地说,他是应用程序主题领域的所有逻辑(即业务逻辑)的持有者。

一切都那么红润吗?


尽管非常简单和透明,使用redux也有许多缺点...

状态为 redux的数据位于一个简单的javascript对象中,可以通过通常的方式获取,您只需要知道路径即可

但是我们如何组织这些数据? 只有两个选项: 平面列表层次模型

对于只有一个计数器的应用程序,平面列表是一个不错的选择。更严重的是,我们需要一个层次结构。 此外,每个级别的数据将比前一个级别具有更少的知识。 一切都是合乎逻辑且可以理解的,但是数据路径变得复杂

例子
const dataHierarchy = { user: { id, name, experience, achievements: { firstTraining, threeTrainingsInRow, }, }, training: { currentSetId, status, totalAttemptsCount, attemptsLeft, mechanics: { ... }, }, }; 

在此,在用户按键下存储用户数据,尤其是成就 。 但是成就并不需要了解用户的其余数据。

以同样的方式,特定的训练机制无需知道用户进行了多少次尝试-这些通常是训练数据。


分层数据结构的存在和对此数据的模块化方法的缺乏导致这样一个事实,即使用该数据的每个地方都必须知道到它的完整路径 。 换句话说,这造成了数据存储结构及其显示结构凝聚,并导致路径重构和/或数据结构重组的困难。

图片

IDE魔术将无济于事
可以说,现在有功能强大的IDE可以使用一条命令来更改路径,但是几乎没有什么可以更改对象的多个嵌套键或了解路径的一部分位于变量中。

另一个挑战是测试。 是的,文档中还有一篇关于Redux测试的文章,但是现在它不是关于测试reduceraction-creater之类的单个工件的。

数据, 动作和约简通常是互连的。 一棵逻辑上相关的数据树通常由数个化简器提供服务 ,这需要一起进行测试。

选择器添加到此列表中,其结果尤其取决于减速器的工作...

通常,您可以测试所有这些,但是必须处理仅通过逻辑和约定互连的一堆工件。

还有,如果我们想出一个结构,例如用户数据,包括好友列表,喜欢的歌曲和其他内容,以及通过动作的reduce更改它们的功能,该怎么办 ? 也许我们甚至为所有这些功能编写了一系列测试。 现在在下一个项目中,我们需要同样的东西...

如何便宜地编码?

图片

寻找解决方案


为了弄清楚如何保留redux的优点并摆脱所描述的缺点,您需要了解它在数据生命周期中所依赖的内容:

  • Action的报告事件,自定义等
  • Reducer动作做出反应并更改或不更改数据状态
  • 数据更改本身就是事件,并且可能导致其他数据更改。

图片

在这种情况下,控制器是一种抽象,可以处理用户操作和存储中的数据更改。 他根本不必是一个单独的班级,通常,他被各个组成部分分散。

将整个Redux动物园合并到一个黑盒子中


但是,如果我们将动作减速器选择器包装在一个模块中,并教它不依赖于数据路径的特定路径怎么办?

例如,如果通过调用实例方法user.addFriend(friendId)提交了所有用户操作,该怎么办 ? 并通过getter获得数据: user.getFriendsCount()

如果我们可以通过简单的导入来导入用户的所有功能呢?

 const userModule from 'node_modules/user-module'; 

方便吗 特别是考虑到这一点,您无需编写一堆额外的代码:
npm redux-module-creator软件包提供了用于创建松耦合, 可重用和经过测试的redux模块的所有功能。

每个模块由一个控制器 ,一个减速器和一个动作组成,并具有以下功能:

  • 通过调用集成器方法集成到商店中,并且要更改集成位置,您只需更改集成器的调用位置及其参数

    图片
  • 控制器与其存储中的部分数据建立连接,并记住一次传递给集成器()的路径。 这消除了在使用数据时知道它的需要。

    图片
  • 控制器是所有必需的选择器,适配器等的支架。
  • 跟踪更改,可以订阅自己数据中的更改
  • reducer可以使用调用上下文-模块的实例,并从中获取属于该模块的actionType 。 这消除了导入一堆动作的需要,并减少了出错的可能性。
  • action获得使用的上下文,因为它们已成为模块实例的一部分:现在,它不仅是trainingFinished ,还包括了readModule.actions.trainingFinished
  • 现在,动作存在于模块名称空间中,它允许您为不同的模块创建具有相同名称的事件
  • 每个模块可以实例化多次,并且每个实例都集成到存储的不同部分中
  • 不同模块实例的action具有不同的actionType-您可以响应特定实例的事件

结果,我们得到了一个黑匣子,它可以管理其数据本身,并具有与外部代码进行通信的句柄。

同时,它是相同的redux ,具有单向数据流,透明性和可预测性。

并且由于这些都是相同的redux和所有相同的reducer ,因此您可以根据它们来构建应用程序域逻辑所需的任何结构。

Source: https://habr.com/ru/post/zh-CN459460/


All Articles