/redux-rx-middleware

simple rxjs integration for redux

Primary LanguageTypeScriptMIT LicenseMIT

redux-rx-middleware

🤪 simple rxjs integration for redux

build status Codecov npm version npm downloads

install

npm i redux-rx-middleware

or

yarn add redux-rx-middleware

dependencies

it supports rxjs 6.x versions

  • redux because it's middleware for redux
  • rxjs because it's rx middleware
  • tslib for cls and esm packages it uses three shaking to decrease bundle size
  • @types/redux-actions for support FSA for redux

usage

add middleware

import { rxMiddleware } from "redux-rx-middleware";

dispatch action with observable stream

import { from } from "rxjs";
import { OBSERVABLE_API, ObservableAction } from "redux-rx-middleware";

export function observableAction(): ObservableAction<number> {
    return {
        type: "ACTION_TYPE",
        meta: {
            [OBSERVABLE_API]: {
                stream: from([1, 2, 3]),
            },
        },
    };
}

For the official integration (from core contributors) with RxJS and Redux, please take a look at redux-observable

This is a simple middleware like redux-promise which adds support Rx.Observable for the actions.

Why not just redux-observable?

first of all, redux-observable uses Epics to describe async actions in redux.

Epic is a function which takes a stream of actions and returns a stream of actions. Actions in, actions out.

so you can feel free to manage your stream of actions with Epics, but this is an additional entity, it brings specials api on top of redux. also you can not dispatch multiple actions - more detils here

this redux-rx-middleware provides 2 things:

  • if has meta has a key with an Observable stream, it will subscribe to this stream
  • it will handle one Observable action to many simple actions with a different state of execution. For example, incoming action:
{
    type: "ACTION_TYPE",
    meta: {
        ["@api/OBSERVABLE_API"]: {
            stream: from([1, 2, 3]),
        },
    },
}

outgoing actions will be

{
    type: "ACTION_TYPE",
    meta: {
        ["@api/OBSERVABLE_API"]: {
            sequence: "start",
        },
    },
}
------------------------------
              |
              |
              V
{
    type: "ACTION_TYPE",
    payload: 1,
    meta: {
        ["@api/OBSERVABLE_API"]: {
            sequence: "next",
        },
    },
}
------------------------------
              |
              |
              V
{
    type: "ACTION_TYPE",
    payload: 2,
    meta: {
        ["@api/OBSERVABLE_API"]: {
            sequence: "next",
        },
    },
}
------------------------------
              |
              |
              V
{
    type: "ACTION_TYPE",
    payload: 3,
    meta: {
        ["@api/OBSERVABLE_API"]: {
            sequence: "next",
        },
    },
}
------------------------------
              |
              |
              V
{
    type: "ACTION_TYPE",
    meta: {
        ["@api/OBSERVABLE_API"]: {
            sequence: "complete",
        },
    },
}

Why does it mapped one async action to many sync actions? In general overview the async action can has one type but different states:

({type: "GET_USERS", sequence: "start" })
                  👇🏼
({type: "GET_USERS", sequence: "done" })
                  👇🏼
({type: "GET_USERS", sequence: "error" })

or as usual in redux applications, different types around the one async action:

({type: "GET_USERS" })
          👇🏼
({type: "GET_USERS_DONE"})
          👇🏼
({type: "GET_USERS_ERROR"})

The benefit of the first flow that in both cases you need to handle this actions in reducer by sequence state or by action type, but you can delegate the creation of the state actions to middleware, don't create additional action by yourself. The main goal of this simple middleware that one async action (with Rx API) has different states and you can just handle it in your reducer.