Is there any place for OOP in redux?
Asked Answered
T

4

24

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...

Transpose answered 30/4, 2017 at 10:51 Comment(5)
Just want to say that OOP doesn't emphasize inheritance. Most of design patterns from GoF use composition + interfaces (except some like template method)Ambulator
If you are working on your personal project and you feel the code you write is easy to read/maintain, of course you can use whatever methodology you preferAmbulator
But I still encourage you to try to think in the functional way. If you read the source code of React and the React 16 rewrite (React fiber), you will notice that the current React codebase is more OOP style, while in React fiber there is no class at allAmbulator
The language is changing, and how people write javascript is also evolving. Functions are lightweight and flexible. You can spend some time reading through the Redux docs and examples to see how beautiful the code is.Ambulator
Also see medium.com/@ustunozgur/…Unconsidered
T
9

I'll answer my own question by describing what I ended up doing, imperfect as it is.

First, instead of regular ES6 class syntax, I've started using stampit, which is uglier that ES6 classes, but much more flexible.

Mainly, though, my complex objects exist in two forms:

  • plain JS objects for the store
  • class (actually stamped) instances for convenience and power of using instance methods.

I use a convention of putting an _underscore in front of all references to the plain objects. My "solution" is kludgy and bad for lots of reasons, but I think trying to use selectors for everything would be worse. If you're curious, here's the place in my code where I "inflate" my plain store objects into instances: https://github.com/Sigfried/vocab-pop/blob/localstorage/src/ducks/conceptSet.js#L292

UPDATE

Turning redux state POJOs into class instances (regular or stampit) is a terrible idea and someone should have stopped me long ago.

I probably should have accepted @markerikson's answer, and maybe the Redux-ORM thing is worth looking at, but I just wanted to say definitively, DON'T DO WHAT I DID. (I always think I'm so smart filling in the "gaps" of technologies I'm learning with clever hacks -- and then I spend painful months cleaning up the mess once I understand why that technology didn't include my hack in the first place.)

Another update

From 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.

Looks like there are good ways to think about combining FP and OOP, and it will, no doubt, use some immutable classes and composition without a lot of inheritance. This series on composition looks like what I needed to learn.

Transpose answered 27/7, 2017 at 11:47 Comment(2)
"and then I spend painful months cleaning up the mess once I understand why that technology didn't include my hack in the first place" - that's a true life lessonEmbrey
only for those who actually learn it :) for me, the real life lesson is about how gullible I continue to be when my brain gets excited about doing something, no matter how often I've been burned by similarly clever ideas in the past.Transpose
E
12

The answer is that it's possible but heavily discouraged and non-idiomatic.

React does rely on classes and single-level inheritance of React.Component to implement stateful components with lifecycles, but you are officially discouraged from doing further levels of inheritance in components.

Redux is built around Functional Programming principles. For a variety of reasons, you are encouraged to keep your state as plain JS objects and arrays, and access/manipulate it using plain functions.

I've certainly seen many libraries that tried to add an OOP layer on top of Redux (such as classes whose methods are turned into action creators and reducers). Those work, but are definitely going against the overall spirit of Redux.

I do actually use a library called 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. Overall, it's an excellent tool for helping manage relationships and normalized data in your Redux store.

Finally, I'm currently working on a blog post that will discuss the actual technical limitations that Redux requires (and why), vs how you are intended to use Redux, vs how it's possible to use Redux. I hope to have that up in the next week or so - keep an eye on http://blog.isquaredsoftware.com .

Epizoon answered 1/5, 2017 at 15:43 Comment(1)
Redux-ORM is very interesting, but what if we want to follow the suggestion of to keep states as plain JS objects and arrays, and access and manipulate it using plain functions, where we would define the creation logic for example? and where to implement the logic for retrieving related object(like author of a book)?Gert
T
9

I'll answer my own question by describing what I ended up doing, imperfect as it is.

First, instead of regular ES6 class syntax, I've started using stampit, which is uglier that ES6 classes, but much more flexible.

Mainly, though, my complex objects exist in two forms:

  • plain JS objects for the store
  • class (actually stamped) instances for convenience and power of using instance methods.

I use a convention of putting an _underscore in front of all references to the plain objects. My "solution" is kludgy and bad for lots of reasons, but I think trying to use selectors for everything would be worse. If you're curious, here's the place in my code where I "inflate" my plain store objects into instances: https://github.com/Sigfried/vocab-pop/blob/localstorage/src/ducks/conceptSet.js#L292

UPDATE

Turning redux state POJOs into class instances (regular or stampit) is a terrible idea and someone should have stopped me long ago.

I probably should have accepted @markerikson's answer, and maybe the Redux-ORM thing is worth looking at, but I just wanted to say definitively, DON'T DO WHAT I DID. (I always think I'm so smart filling in the "gaps" of technologies I'm learning with clever hacks -- and then I spend painful months cleaning up the mess once I understand why that technology didn't include my hack in the first place.)

Another update

From 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.

Looks like there are good ways to think about combining FP and OOP, and it will, no doubt, use some immutable classes and composition without a lot of inheritance. This series on composition looks like what I needed to learn.

Transpose answered 27/7, 2017 at 11:47 Comment(2)
"and then I spend painful months cleaning up the mess once I understand why that technology didn't include my hack in the first place" - that's a true life lessonEmbrey
only for those who actually learn it :) for me, the real life lesson is about how gullible I continue to be when my brain gets excited about doing something, no matter how often I've been burned by similarly clever ideas in the past.Transpose
U
1

This question is a bit opinion-based but let's concentrate on the core points.

There is no contradiction between functional programming and OOP. You just need to use the same programming patterns. There is no problem to use classes (with inheritance) in functional programming, if you keep them immutable.

To prove my point, the popular library Immutable.js that is used by many people to keep state in redux is composed from classes. And those classes have inheritance (e.g. OrderedSet extends Set).

Also note that most React components are classes and they use inheritance, too (... extends React.Component).

Undergarment answered 30/4, 2017 at 12:4 Comment(6)
Thank you. But this doesn't quite answer my question. React components inherit from React.Component, but they're not recommended to inherit from each other. If I was writing a reusable library I would feel fine using OOP like your examples, but my question is about storing and manipulating data held in a Redux store for use in React components. Sorry I was less than clear.Transpose
@Transpose Why are they not recommended to inherit from each other? There is nothing wrong about inheritance of components. Also there is nothing wrong when using classes in redux, (e.g. Date). The only requirement is that you don't mutate them directly (keep them immutable).Undergarment
facebook.github.io/react/docs/composition-vs-inheritance.html says: "we recommend using composition instead of inheritance to reuse code between components." My question is more about Redux. The discussion I referenced (#32353482) seems to frown on it. And all the example code I've seen uses selectors, which are a very different way of structuring data from OOP classes. I would like to use OOP classes, but I'm not sure how to relate them to the store. I'll edit my question again and add a use case.Transpose
@Transpose That's why I am saying it is opinion based. Some people will tell you that any use of inheritance in functional programming is evil. Some people will tell you that OOP and functions are orthogonal and can be used together.Undergarment
Yeah, especially the way I have the title phrased. I think what I'm asking/saying is: given that it is evil (as evidenced by my own experience), and also, if not orthogonal, at least possible and painful to do without, is there some way to combine them (in the case of redux) that minimizes the evil. The answer I just added gives my current approach to combining them. So, really what I want is not opinions so much as ideas of how to do redux programming without totally sacrificing the advantages of organizing code with method-laden, composable objects (like with OOP)...Transpose
Maybe the answer is even something like: Don't mix them, you don't have to because feature x (HOF?) of functional programming (and redux) solves the problems you're trying to solve with classes. But if that's the answer, I need some pointer to decent explanations of how to really do that.Transpose
F
0

Redux programming style is "message passing", and that is basically OOP "In computer science, message passing is a technique for invoking behavior (i.e., running a program) on a computer. The invoking program sends a message to a process (which may be an actor or object) and relies on that process and its supporting infrastructure to then select and run some appropriate code. Message passing differs from conventional programming where a process, subroutine, or function is directly invoked by name. Message passing is key to some models of concurrency and object-oriented programming." From wikipedia

Faultfinding answered 17/2 at 0:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.