22

I've been using object-oriented programming practices for 25 years and trying to move toward functional programming for the last 5 years, but my mind always goes towards OOP when I'm trying to do something complex and, especially now that ES6 supports decent OOP syntax, that's the natural way for me to build stuff.

I'm now learning Redux and I understand (c.f. How to put methods onto the objects in Redux state?) 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 oop classes).

But is there any place in the React/Redux ecosystem for class objects with methods and inheritance?

I guess, to sort of answer my own question, OOP classes encourage the addition of data properties and operations on the data in the same place, which is nice for readability, but doesn't fit well with pure functions and immutable data.

If I was going to use OOP, would I need to chuck the idea of having my instances persist and maintain state for any amount of time? Like, every time I want to use one, I would instantiate it from store data, use whatever methods I want, and throw it away? That might obviate a lot of the impetus to use OOP classes. But if I keep instances around, I'll have headaches keeping them synced with the store.

So, is the answer to always use selectors when I'm tempted to use methods and always use composition when I'm tempted to use inheritance? Specifically, I mean when storing and manipulating data held in a Redux store for use in React components. And, if so, where should it fit in? Connected to selectors? Immediately disposable like I suggested?


Adding my use case for clarity: My data is basically a huge graph: lots of objects with lots of properties and lots of relationships between objects. It's read only, but complex. My objects are called "concepts".

Before making the (probably foolish) decision to migrate to Redux, I used classes to structure and represent concepts, sets of concepts, and relationships between concepts. My classes included async api logic to fetch concept sets, information about each concept, and information about other concepts that each concept is related to. If the user chose to drill down, the classes would recursively fetch and instantiate new concept sets. The Redux documentation recommends flat, normalized structures for nested data (http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html) which is probably wise for storage, but my OOP model was good for traversing sections of the graph and stuff. I have a hard time wrapping my head around using selectors and immutable state that might involve nesting, potentially with cycles, or needing to make async calls for more data.

I'm successfully using https://redux-observable.js.org/ for the api stuff.

Maybe @Sulthan's answer is right: I should feel free to use OOP techniques in my Redux application. But it still seems weird. I can't keep my objects around because if the store changes (more data is fetched, for instance), my objects can get stale. If my objects are nested but my store is normalized, I'll instantiate them (from selectors) when I need them and make sure not to keep them around...

4

3 回答 3

11

答案是它是可能的,但非常不鼓励和不习惯。

React 确实依赖于类和单级继承React.Component来实现具有生命周期的有状态组件,但官方不鼓励您在组件中进行进一步级别的继承。

Redux 是围绕函数式编程原则构建的。出于各种原因,我们鼓励您将状态保留为纯 JS 对象和数组,并使用纯函数访问/操作它。

我当然见过许多试图在 Redux 之上添加 OOP 层的库(例如其方法变成动作创建者和归约器的类)。这些工作,但肯定违背了 Redux 的整体精神。

我确实使用了一个名为Redux-ORM的库,它允许您定义模型类,这些类充当商店中普通 JS 对象的外观。然而,与我见过的许多其他库不同,它Redux 一起工作,而不是试图改变 Redux 的行为方式。我在我的博客文章Practical Redux, Part 1: Redux-ORM Basics and Practical Redux, Part 2: Redux-ORM Concepts and Techniques中讨论了 Redux-ORM 是如何工作的,我如何使用它,以及为什么它仍然相当地道。总体而言,它是帮助管理 Redux 存储中的关系和规范化数据的出色工具。

最后,我目前正在写一篇博文,讨论 Redux 需要的实际技术限制(以及原因),以及您打算如何使用 Redux,以及如何使用Redux。我希望在接下来的一周左右能完成这项工作——请留意http://blog.isquaredsoftware.com

于 2017-05-01T15:43:39.703 回答
8

我将通过描述我最终做了什么来回答我自己的问题,尽管它并不完美。

首先,我开始使用stampit代替常规的 ES6 类语法,它比 ES6 类更丑陋,但更灵活。

不过,我的复杂对象主要以两种形式存在:

  • 商店的普通 JS 对象
  • 类(实际上是加盖的)实例,以便使用实例方法的方便和强大。

我使用的约定是在对普通对象的所有引用之前放置一个 _underscore。由于很多原因,我的“解决方案”很笨拙而且很糟糕,但我认为尝试对所有事情都使用选择器会更糟。如果你好奇,这里是我的代码中我将普通存储对象“膨胀”为实例的地方:https ://github.com/Sigfried/vocab-pop/blob/localstorage/src/ducks/conceptSet.js# L292

更新

将 redux 状态 POJO 转换为类实例(常规或 stampit)是一个糟糕的想法,早就应该有人阻止我。

我可能应该接受@markerikson 的回答,也许 Redux-ORM 的东西值得一看,但我只想明确地说,不要做我做过的事。(我一直认为我很聪明地用聪明的 hack 填补了我正在学习的技术的“空白”——然后,一旦我明白了为什么该技术没有将我的 hack 包含在第一名。)

另一个更新

来自作曲软件:简介

我们不会说函数式编程比面向对象编程更好,或者你必须选择其中之一。OOP 与 FP 是错误的二分法。近年来,我看到的每一个真正的 Javascript 应用程序都广泛地混合了 FP 和 OOP。

看起来有一些很好的方法可以考虑将 FP 和 OOP 结合起来,毫无疑问,它将使用一些不可变的类和组合,而无需大量继承。这个关于作文的系列看起来像是我需要学习的。

于 2017-07-27T11:47:57.263 回答
1

这个问题有点基于意见,但让我们专注于核心点。

函数式编程和 OOP 之间没有矛盾。您只需要使用相同的编程模式。如果您保持它们不可变,那么在函数式编程中使用类(具有继承)是没有问题的。

为了证明我的观点,许多人用来在 redux 中保持状态的流行库Immutable.js是由classes组成的。并且这些类具有继承性(例如OrderedSet extends Set)。

另请注意,大多数React组件都是,它们也使用继承 ( ... extends React.Component)。

于 2017-04-30T12:04:00.270 回答