DIY Redux with RxJS: Part 3

901 reads

@ onerizafer Öner Zafer Software engineer, senior frontend developer based in Berlin. Hard Sci-Fi addict.

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 HOCs 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, HOCs 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 HOCs you may follow the link below but if you are in the mood TL;DR I will continue with a summary of it:

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

Share this story @ onerizafer Öner Zafer Read my stories Software engineer, senior frontend developer based in Berlin. Hard Sci-Fi addict.

Tags