redux 中有 OOP 的位置吗?
Is there any place for OOP in redux?
我已经使用面向对象编程实践 25 年,并在过去 5 年中尝试转向函数式编程,但当我尝试做一些复杂的事情时,我的想法总是倾向于 OOP,尤其是现在 ES6 支持体面的 OOP 语法,这是我构建东西的自然方式。
我现在正在学习 Redux 并且我明白了 (c.f。) that it's a no-no to put class instances in your reducers; and the recommended method for computing on top of plain reducer state is by using selectors (e.g., via reselect). And, of course, React recommends composition over inheritance (https://facebook.github.io/react/docs/composition-vs-inheritance.html, )。
但是 React/Redux 生态系统中是否有 class 具有方法和继承的对象的位置?
我想,为了回答我自己的问题,OOP classes 鼓励在同一个地方添加数据属性和对数据的操作,这对可读性很好,但不适合纯函数和不可变数据很好。
如果我打算使用 OOP,我是否需要放弃让我的实例持续存在并在任意时间内保持状态的想法?就像,每次我想使用一个,我会从存储数据中实例化它,使用我想要的任何方法,然后扔掉它?这可能会消除很多使用 OOP classes 的动力。但是,如果我保留实例,我会很头疼让它们与商店同步。
那么,当我想使用方法时总是使用选择器,而当我想使用继承时总是使用组合,这就是答案吗?具体来说,我指的是存储和操作 Redux 存储中保存的数据以用于 React 组件。而且,如果是这样,它应该放在哪里?连接到选择器?像我建议的那样立即丢弃?
为清楚起见添加我的用例:我的数据基本上是一个巨大的图表:许多对象具有许多属性以及对象之间的许多关系。它是只读的,但很复杂。我的对象名为 "concepts"。
在做出迁移到 Redux 的(可能是愚蠢的)决定之前,我使用 classes 来构建和表示概念、概念集以及概念之间的关系。我的 classes 包括用于获取概念集的异步 api 逻辑、有关每个概念的信息以及有关每个概念相关的其他概念的信息。如果用户选择向下钻取,classes 将递归地获取并实例化新的概念集。 Redux 文档为嵌套数据 (http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html) 推荐了扁平的规范化结构,这对于存储来说可能是明智的,但我的 OOP 模型非常适合遍历图形的各个部分和其他内容。我很难全神贯注地使用选择器和不可变状态,这可能涉及嵌套,可能带有循环,或者需要对更多数据进行异步调用。
我成功地将 https://redux-observable.js.org/ 用于 api 内容。
也许@Sulthan 的回答是正确的:我应该在我的 Redux 应用程序中随意使用 OOP 技术。但它仍然看起来很奇怪。我无法保留我的对象,因为如果存储发生变化(例如,获取更多数据),我的对象可能会变得陈旧。如果我的对象是嵌套的但我的存储是规范化的,我会在需要时实例化它们(从选择器)并确保不要将它们保留在周围......
这个问题有点主观,但让我们集中讨论核心点。
函数式编程和OOP之间并不矛盾。您只需要使用相同的编程模式。在函数式编程中使用 类(带继承)没有问题,如果你保持它们不可变的话。
为了证明我的观点,许多人用来在 redux 中保持状态的流行库 Immutable.js 由 类 组成。那些 类 具有继承性(例如 OrderedSet extends Set
)。
另请注意,大多数 React
组件都是 类 并且它们也使用继承 (... extends React.Component
)。
答案是可能,但强烈建议不要这样做,而且不合常理。
React 确实依赖 类 和 React.Component
的单级继承来实现具有生命周期的有状态组件,但官方不鼓励您在组件中进行更多级别的继承。
Redux 是围绕函数式编程原则构建的。出于各种原因,我们鼓励您将状态保持为纯 JS 对象和数组,并且 access/manipulate 使用纯函数。
我确实看到过许多试图在 Redux 之上添加 OOP 层的库(例如 类,其方法被转换为 action creators 和 reducers)。那些工作,但绝对违背了 Redux 的整体精神。
我确实使用了一个名为 Redux-ORM, which does allow you to define Model classes that act as a facade over the plain JS objects in your store. However, unlike many of the other libraries that I've seen, it works with Redux rather than trying to change how Redux behaves. I discussed how Redux-ORM works, how I use it, and why it's still reasonably idiomatic, in my blog posts Practical Redux, Part 1: Redux-ORM Basics and Practical Redux, Part 2: Redux-ORM Concepts and Techniques 的库。总体而言,它是帮助管理 Redux 商店中的关系和规范化数据的出色工具。
最后,我目前正在写一个博客 post,它将讨论 Redux 需要的实际技术限制(以及为什么),与您的 意图使用 Redux,与 可能 使用 Redux 的对比。我希望能在下周左右完成 - 请关注 http://blog.isquaredsoftware.com .
我将通过描述我最终所做的事情来回答我自己的问题,尽管它并不完美。
首先,我开始使用 stampit 而不是常规的 ES6 class 语法,它比 ES6 classes 更丑陋,但更灵活。
不过,我的复杂对象主要以两种形式存在:
- 商店的纯 JS 对象
- class(实际标记)实例,以方便和使用实例方法。
我使用的惯例是在所有对普通对象的引用之前放置一个 _underscore。由于很多原因,我的 "solution" 很笨拙,但我认为尝试对所有内容都使用选择器会更糟。如果你很好奇,这里是我的代码中我 "inflate" 将我的普通存储对象放入实例中的地方:https://github.com/Sigfried/vocab-pop/blob/localstorage/src/ducks/conceptSet.js#L292
更新
将 redux 状态 POJO 变成 class 实例(常规或 stampit)是一个糟糕的主意,早就应该有人阻止我了。
我可能应该接受@markerikson 的回答,也许 Redux-ORM 值得一看,但我只想明确地说,不要做我做的事。 (我一直认为我很聪明地填补了我正在学习的 "gaps" 技术的聪明技巧——然后,一旦我明白为什么该技术不包括我的技巧,我就会花费痛苦的几个月来清理烂摊子首先。)
另一个更新
来自Composing Software: An Introduction:
What we won’t do is say that functional programming is better than
object-oriented programming, or that you must choose one over the
other. OOP vs FP is a false dichotomy. Every real Javascript
application I’ve seen in recent years mixes FP and OOP extensively.
看起来有很好的方法来考虑结合 FP 和 OOP,毫无疑问,它将使用一些不可变的 classes 和组合,而无需大量继承。这个关于作文的系列看起来像我需要学习的东西。
我已经使用面向对象编程实践 25 年,并在过去 5 年中尝试转向函数式编程,但当我尝试做一些复杂的事情时,我的想法总是倾向于 OOP,尤其是现在 ES6 支持体面的 OOP 语法,这是我构建东西的自然方式。
我现在正在学习 Redux 并且我明白了 (c.f。
但是 React/Redux 生态系统中是否有 class 具有方法和继承的对象的位置?
我想,为了回答我自己的问题,OOP classes 鼓励在同一个地方添加数据属性和对数据的操作,这对可读性很好,但不适合纯函数和不可变数据很好。
如果我打算使用 OOP,我是否需要放弃让我的实例持续存在并在任意时间内保持状态的想法?就像,每次我想使用一个,我会从存储数据中实例化它,使用我想要的任何方法,然后扔掉它?这可能会消除很多使用 OOP classes 的动力。但是,如果我保留实例,我会很头疼让它们与商店同步。
那么,当我想使用方法时总是使用选择器,而当我想使用继承时总是使用组合,这就是答案吗?具体来说,我指的是存储和操作 Redux 存储中保存的数据以用于 React 组件。而且,如果是这样,它应该放在哪里?连接到选择器?像我建议的那样立即丢弃?
为清楚起见添加我的用例:我的数据基本上是一个巨大的图表:许多对象具有许多属性以及对象之间的许多关系。它是只读的,但很复杂。我的对象名为 "concepts"。
在做出迁移到 Redux 的(可能是愚蠢的)决定之前,我使用 classes 来构建和表示概念、概念集以及概念之间的关系。我的 classes 包括用于获取概念集的异步 api 逻辑、有关每个概念的信息以及有关每个概念相关的其他概念的信息。如果用户选择向下钻取,classes 将递归地获取并实例化新的概念集。 Redux 文档为嵌套数据 (http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html) 推荐了扁平的规范化结构,这对于存储来说可能是明智的,但我的 OOP 模型非常适合遍历图形的各个部分和其他内容。我很难全神贯注地使用选择器和不可变状态,这可能涉及嵌套,可能带有循环,或者需要对更多数据进行异步调用。
我成功地将 https://redux-observable.js.org/ 用于 api 内容。
也许@Sulthan 的回答是正确的:我应该在我的 Redux 应用程序中随意使用 OOP 技术。但它仍然看起来很奇怪。我无法保留我的对象,因为如果存储发生变化(例如,获取更多数据),我的对象可能会变得陈旧。如果我的对象是嵌套的但我的存储是规范化的,我会在需要时实例化它们(从选择器)并确保不要将它们保留在周围......
这个问题有点主观,但让我们集中讨论核心点。
函数式编程和OOP之间并不矛盾。您只需要使用相同的编程模式。在函数式编程中使用 类(带继承)没有问题,如果你保持它们不可变的话。
为了证明我的观点,许多人用来在 redux 中保持状态的流行库 Immutable.js 由 类 组成。那些 类 具有继承性(例如 OrderedSet extends Set
)。
另请注意,大多数 React
组件都是 类 并且它们也使用继承 (... extends React.Component
)。
答案是可能,但强烈建议不要这样做,而且不合常理。
React 确实依赖 类 和 React.Component
的单级继承来实现具有生命周期的有状态组件,但官方不鼓励您在组件中进行更多级别的继承。
Redux 是围绕函数式编程原则构建的。出于各种原因,我们鼓励您将状态保持为纯 JS 对象和数组,并且 access/manipulate 使用纯函数。
我确实看到过许多试图在 Redux 之上添加 OOP 层的库(例如 类,其方法被转换为 action creators 和 reducers)。那些工作,但绝对违背了 Redux 的整体精神。
我确实使用了一个名为 Redux-ORM, which does allow you to define Model classes that act as a facade over the plain JS objects in your store. However, unlike many of the other libraries that I've seen, it works with Redux rather than trying to change how Redux behaves. I discussed how Redux-ORM works, how I use it, and why it's still reasonably idiomatic, in my blog posts Practical Redux, Part 1: Redux-ORM Basics and Practical Redux, Part 2: Redux-ORM Concepts and Techniques 的库。总体而言,它是帮助管理 Redux 商店中的关系和规范化数据的出色工具。
最后,我目前正在写一个博客 post,它将讨论 Redux 需要的实际技术限制(以及为什么),与您的 意图使用 Redux,与 可能 使用 Redux 的对比。我希望能在下周左右完成 - 请关注 http://blog.isquaredsoftware.com .
我将通过描述我最终所做的事情来回答我自己的问题,尽管它并不完美。
首先,我开始使用 stampit 而不是常规的 ES6 class 语法,它比 ES6 classes 更丑陋,但更灵活。
不过,我的复杂对象主要以两种形式存在:
- 商店的纯 JS 对象
- class(实际标记)实例,以方便和使用实例方法。
我使用的惯例是在所有对普通对象的引用之前放置一个 _underscore。由于很多原因,我的 "solution" 很笨拙,但我认为尝试对所有内容都使用选择器会更糟。如果你很好奇,这里是我的代码中我 "inflate" 将我的普通存储对象放入实例中的地方:https://github.com/Sigfried/vocab-pop/blob/localstorage/src/ducks/conceptSet.js#L292
更新
将 redux 状态 POJO 变成 class 实例(常规或 stampit)是一个糟糕的主意,早就应该有人阻止我了。
我可能应该接受@markerikson 的回答,也许 Redux-ORM 值得一看,但我只想明确地说,不要做我做的事。 (我一直认为我很聪明地填补了我正在学习的 "gaps" 技术的聪明技巧——然后,一旦我明白为什么该技术不包括我的技巧,我就会花费痛苦的几个月来清理烂摊子首先。)
另一个更新
来自Composing Software: An Introduction:
What we won’t do is say that functional programming is better than object-oriented programming, or that you must choose one over the other. OOP vs FP is a false dichotomy. Every real Javascript application I’ve seen in recent years mixes FP and OOP extensively.
看起来有很好的方法来考虑结合 FP 和 OOP,毫无疑问,它将使用一些不可变的 classes 和组合,而无需大量继承。这个关于作文的系列看起来像我需要学习的东西。