First, we need to take a look in a nutshell what it’s about Reactive Programming (Rx), for that we can represent as the following diagram:
The line that we see, it’s about time, and the colors bolls are balls which represent the user interactions/actions/events.
If we combined these two concepts, we could say that the combination of these circle colors and lines is Streams.
Okay, somebody could say that it’s maybe Stream is a new concept, but it’s not. The Stream exists event before Rx lib exists, in Smalltalk (1972) we already have ones as a concept. Also, Common Lisp has as well.
You can think of Streams as an infinite array of actions/events. If you want to go deeper about that, I suggest taking a look at this amazing doc from Andre Staltz, about Reactive programming (creator of CycleJS) or if you prefer one of his amazing talks.
The fundamental part here is to understand two things:
- Streams (Action/Events + line of time)
- Observable.
About observable, I strongly recommend taking a look at this great video from Christopher Okhravi (this guy also has amazing videos out there about general concepts, principles, all really cool stuff). Again nothing new here as well, just implementing an old design pattern Observer pattern from a GoF book (1994).
Okay, it was just a shallow introduction about Rx, let jump how does it work in React and Redux-Observable application.
Redux-Observable
Redux-Observable it’s just a middleware of redux, like Redux-thank. That helps us to handler side-effects in different ways asynchronously, with cancelations, and much more.
Just to describe at a high level this library, think Redux-Observable like a way that uses RxJS but in Redux.
Another important note here is the way that affects redux-observable our Redux app. The redux-observable only concern in the Actions layer or Action creator (side-effects). Basically, the idea is intercept actions and produce some side-effects and then produce normal actions that will continue with the normal flow of a redux application.
As as you see, only intercept actions, produce side-effect, and then return plain actions to our reducers. How handler these side-effects is called Epic.
- Epic: is a core concept in Redux Observable, which it essentially means:
“given Stream, maps some other action/s into another stream”
So, for the archive that, Redux Observable, use the following two function to Hook it up into Redux flow:
import {
createEpicMiddleware,
combineEpics
} from 'redux-observable'const epic1 = action$ => actions$.ignoreElements(); // RxJS
const epic2 = action$ => actions$.ignoreElements(); // RxJSconst rootEpic = combineEpics(epic1, epic2); // redux-observerconst store = createStore( // redux
rootReducer, // redux
applyMiddleware( // redux
createEpicMiddleware(rootEpic) // redux-observer
);
So basically, rootEpic
is a function, which was build from as a result of use combine our actions, and it was passed in to createEpicMiddleware
which return a normal redux middleware.
NOTE: You are able to implement other libraries for handler Streaming, like Most.js, Ramda, etc.
Let’s take a look at a more interesting Epic example:
const $doubleClick = actions$ => { const click$ = action$.ofType('CLICK'); return click$
.buffer($click.debounceTime(250))
.filter(clicks => clicks.length === 2)
.map(clicks => ({type: "DOUBLE_CLICK"}) // convert stream into plain normal action
.take(1);
}
Just, listening to the mainstream of the type of action CLICK,
and return a plain action DOUBLE_CLICK
if you detect two clicks in less than 250ms :)
So the power about Epic is, when you have a really complicate UI, in when you need to perform several tasks, and need to organize with each other each task, and some of the tasks depend to finish some other task and need to orchestrate this interaction. For instance, then produce some results from that combination of computation. So you can say an Epic, is like a “scheduled action or some planned action”.
So at architecture design level, Rx programming, it is about how your application behave instead of how does it implement the different features of our application.
Because, each functionally/feature, that you will need to implement it, will observe, these events/actions, and then will produce from there your functionality needed.
Most of this material was extracted from this amazing talk and also contain some personal comments from my side.