DEV Community

Öner Zafer
Öner Zafer

Posted on • Originally published at hackernoon.com on

DIY Redux with RxJS: Part 3

Photo by Steve Halama on Unsplash

So far, in the previous two posts, I covered the topics “How to Create a Redux Library with RxJS” and “How to Write Redux Middlewares”. Before starting the 3rd and the last part of the series, I would recommend you to check the first 2 parts below:

In this part, I will create a HOC (Higher Order Component) to connect RxDx with React.Component. I assume that you already know about React.Component, so that I won’t be explaining it again. But I need to explain HOC s which is the heart of what I will demonstrate below.

What the heck are these Higher-Order Components?

Basically, HOC is a class decorator. But what is a class decorator? Sometimes while we are coding we start feeling that all classes we create have the same basic properties. At that point, we realize that we are repeating ourselves which is completely against the DRY rule. So we start trying to find a way to isolate the repeating parts. Or while we are coding we need to add a feature to our classes without changing the implementation of it. In both cases, our purpose is to achieve code sharing between classes. So the very best answer to our problem described above is kind of a class factory function.

Class factory functions are functions which returns a class. (welcome to the weirdness of the javascript world).

// the most primitive description of a class factory function
function **giveMeClass** (someArguments) {
   return class **SomeDynamicallyGeneratedClass** {}
}

The primitive class factory function is not a class decorator yet. To be a class decorator, a class factory function requires a class as an argument and the returning/produced class will be an extended version of the input class:

//enhance decorator
function **ehance** (targetClass) {
   return class **EnhancedClass** extends **targetClass** {
      constructor() {
         this.isEnhanced = true;
      }
}

//usage in enviroment where experimental decorators allowed
@enhace
class **Normal** {}

console.log(new Normal().isEnhanced) // true

//usage in enviroment where experimental decorators not allowed
class **Normal** {}

const **Enhanced** = enhance(Normal);

console.log(new Enhanced().isEnhanced) // true

As it’s mentioned before, HOC s are also decorators and works exactly the same way as a class decorator. A HOC takes a React component -which is a class- and returns another React component. For detailed documentation of HOC s you may follow the link below but if you are in the mood TL;DR I will continue with a summary of it:

Higher-Order Components - React

Now it’s time to extend the decorator example above to make it a HOC by replacing class input and output with a React Component. I have to mention that using React component as input/output gives us a couple of opportunities, but I will continue with recommended one which is wrapping the original component with another:

// a primitive HOC which makes A component great nothing :)
function **makeGreat** (WrappedComponent) {
   return class **EnhancedComponent** extends **React.Component** {
      constructor() {
         this.state = {isGreat: true}
      }

      render() {
         return < **WrappedComponent** {...this.props, ...this.state} />
      }
   }
}

// usage
class **LameComponent** extends **React.Component** {
   render() {
      return <div>{this.props.isGreat? 'Great': 'Lame'}</div>
   }
}

const **GreatComponent** = **makeGreat** (LameComponent);

< **LameComponent** /> // this will render <div>Lame</div>
< **GreatComponent** /> // this will render <div>Great</div>

// alternative usage if experimental decorators are allowed
@makeGreat
class **LameComponent** extends **React.Component** {
   render() {
      return <div>{this.props.isGreat? 'Great': 'Lame'}</div>
   }
}

< **LameComponent** /> // this will render <div>Great</div>

I believe we need to go deeper right now, so buckle up. At this point, you still don’t know about RxDx you may check it from this link because I will explain how to connect RxDx to React.Component.

Stitching the RxDx with a React Component: A real-life example of HOC

Did you remember the selectors from Part 2 and store.select function from Part 1? This is the moment they will come in handy since we will use them as the cement between RxDx and React.Component. Store.select get a selector or a series of strings and return an observable to allow us to be notified of state updates. We can subscribe to all of these observables in componentDidMount and on each update we can re-render the component by using forceUpdate and to prevent memory leaks we have to unsubscribe from all subscriptions in componentWillUnmount:

class **SomeUglyComponent** extends **Component** {
**componentDidMount** () {
      this.subscription = store
          .select(someSelector)
          .subscribe((update) => {
             this.setState({data: update});
             this.forceUpdate();
   }
**componentWillUnmount** () {
      this.subscription.unsubscribe();
   }
**render** () {
      return (<div>{ this.state.data }</div>);
   }
}

Ok, I have to admit that this will work but SomeUglyComponent is not that ugly as we have only one of it. Let’s imagine we have to implement such components tens of, hundreds of times. Now it seems uglier, right? A generalized solution for this repeating task is required and the SomeUglyComponent should be as simple as possible so we can call it SimpleAndNiceComponent. The desired SimpleAndNiceComponent would be as follows:

const **SimpleAndNiceComponent** = **connect** ({
   data: store.select(someSlector)
})(({data}) => <div> {data} </div>);

//or
@ **connect** ({
   data: store.select(someSlector),
})
class **SimpleAndNiceComponent** extends **Component** {
**render** () {
      const {data} = this.props;
      return <div> {data} </div>;
   }
}

So how do we achieve this simplicity and how we wire observables to props? The answer is hidden inside the HOC s. We need one step further than simple HOC , what we need is a HOC factory. I will implement a connect function which takes observables and returns a HOC which takes a React.Component and returns an enhanced/wrapped React.Component:

For the sake of clarity, the example above is simplified. For sure, if we need to use it for production, we have to think more about edge usage cases and debuggability and also error prevention and recovery strategies. Even though it’s a simplification this code piece will do the trick and will allow us to write simpler components with a generalized connect mechanism with async nature of observables and sync nature of React.Component.

Final, final, final words

Yes, finally we have all in one RxJS powered Redux equivalent library. All I can say that I learned a lot working on this experiment and I wanted to share it hoping that my journey will help others. Next steps will be using it, growing it and loving it!

If you managed to read up to this point and bared with me through this experiment, I will be extremely glad. A big thanks to you! Please don’t forget to clap or drop your comment below.

If you want to contribute to my experiment and help me to make it a real library which can possibly solve real-life problems, don’t hesitate to click the link below and contribute. You will be only welcomed!

GitHub logo onerzafer / rxdx

redux like library for react based on rxjs

RxDx

RxDx is a Rxjs base Redux like state management library and it icludes all necessary tooling inside.

heaviliy inspired from great libraries redux, react-redux, redux-observable and ngrx

Motivation

Basic idea behind this library is bringin the full power of RxJS into react world with a familiar interface of Redux withiot relying on some middleware jungle of libraries.

Installation

npm install rxdx --save

or

yarn add rxdx

How to Use

Store

Unlike react-redux RxDx does not rely on context so no need to introduce Provider on App.js. But on the contrary it requires an exported Store to function. In these sample codes it is suggested to have a saperate store.js for initializing the Store and improting it when neccessary. Intentionally the interface of createStore is just like react-redux interface.

//store.js
import {
  createStore,
  applyMiddleware,
  combineReducers,
  devToolsMiddleware,
  effectsMiddleware
} from "rxdx/lib/rxdx";
import { todoEffects

Top comments (0)