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...
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 .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With