• Stars
    star
    113
  • Rank 310,115 (Top 7 %)
  • Language
    JavaScript
  • Created almost 9 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

RxJS implementation of Saga pattern for redux

redux-saga-rxjs

NPM version Dependencies Build status Downloads

RxJS implementation of Saga pattern for redux

This project is no longer updated and mantained, it was intended as proof of concept, there is a real library with awesome documentation & API, please have a look at redux-observable.

Introduction

Redux is great, long running transactions are problematic

Redux gives us great power but with great power comes great responsibility. It's possible to build amazing, extensible, robust and scalable architecture, yet it's not as easy as it looks, because there are some unknowns which hasn't been fully solved and proven e.g. Local component State / Side effects / Long running transactions etc. One common problem that probably every developer will sooner or later have to face is communication with an API.

Reading through the Redux docs will guide you to use thunk-middleware. Thunk middleware allows you to dispatch function instead of object, this is cool because the function gets called providing dispatch and getState as arguments, therefore it allows you to call the API inside the function and dispatch an action holding payload when API response arrives - this is an asynchronous operation.

const apiAction = (dispatch, getState) => {
  dispatch({type: 'STARTED_LOADING_DATA'});

  api().then(response => dispatch({type: 'DATA_LOADED', response}));
}

const view = dispatch => (
  <button dispatch={() => dispatch(apiAction)}>Click to Load data</button>
);

The example above is something we could call long running transaction, in other words it's some kind of logic group which groups more than single Action. Long running transactions does not necessarily need to be Asynchronous, in this example the long running transaction is also asynchronous because those actions are not dispatched synchronously in sequence.

const synchronousLongRunningTransaction = (dispatch, getState) => {
  dispatch({type: 'FOO'});

  if (getState().foo === 1) {
    dispatch({type: 'BAR'});
  }
}

The example above is also a long running transaction yet it's not asynchronous and does not yield any side effects.

What is Saga pattern?

Saga is a pattern for orchestrating long running transactions... TODO

Why is Saga good and probably mandatory for Redux?

Redux is just predictable state container. We can say that all it does (and it does very well) is holding refrence of your application state and providing interface to mutate it (dispatching actions) in single transaction. Whenever any action (interaction with the UI) is dispatched, the reducer is called with provided application state and action to perform the mutation. The simplest model of Redux is State Machine because it defines two concepts: States and Transitions where in context of Redux - States are references to application state snapshot in specific time and Transitions are Actions.

State Machine is missing one important piece and it's the transition history. State machine knows current state but it doesn't know anything about how it got to the state, it's missing Sequence of transitions. However, the Sequence of transitions is often very important information. Let's imagine you want to withdraw money from ATM, the first thing you need to do is enter your credit card and then enter the PIN. So the sequence of transitions could be as follows: WAITING_FOR_CREDIT_CARD -> CARD_INSERTED -> AUTHORIZED or REJECTED but we would like to allow user enter invalid PIN 3 times before rejecting.

So first naive approach would be model of State machine which covers all the possible states and transitions between them:

atm-1

As you can see, for simple use case the State machine is quite complex, now let's have a look at the State machine which would probably correspond with the way you'd implemented it in Redux.

atm-2

You might have spotted something ugly in the diagram and it's the counter of attempts in the form of intermediate state. Yes, in traditional Redux you'd have to keep the information in your application state because State Machine does not allow you to keep track of transitions history. And that's exactly the point where Saga comes into play.

Long running transaction in context of Redux means specific sequence of dispatched actions, without Saga there's no other way to know about the sequence but storing intermediate state in your application state.

const reducer = (appState, { type }) {
  switch (type) {
    case 'VALID_PIN_ENTERED':
      return { ...appState, authorized: true };

    case 'PIN_REJECTED':
      if (appState.attempt >= 2) {
        return { ...appState, authFailure: true };
      } else {
        return { ...appState, attempt: appState.attempt + 1 };
      }

    default:
      return appState;
  }
}

However, using Saga we don't need to store the intermediate state in the application state and reducers because all this lives in Saga. The reducer would be just a simple projector of your Domain Events (Actions), where the projection is application state.

const reducer = (appState, { type }) {
  switch (type) {
    case 'AUTHORIZED':
      return { ...appState, authorized: true };

    case 'REJECTED':
      return { ...appState, authFailure: true };

    default:
      return appState;
  }
}

Comparison

Is thunk-middleware Saga pattern?

[...TODO]

What's the relation between Saga pattern and Side effects?

So why people say that Saga is a great pattern for Side effects? Let's take an API call as example - you'll probably have one action (API_REQUESTED) dispatched when user clicks the button to load data, which presumably displays loading spinner and another action to process the response (API_FINISHED) and this is all in single, long running transaction which Saga can handle.

We need to distinguish between Side effects and Asynchronous long running transaction. The former stands for some effect which is not the primary goal of the calling function (mutation of some external state / calling XHR / logging to console...) while the latter stands for asynchronous sequence of actions which is some logical group (transaction). Saga solves the latter, it's just an implementation detail that it's capable of solving side effects. We could still say that it should be forbidden to perform side effects in Sagas as it is in Reducers - just a minor implementation detail.

Difference between redux-saga-rxjs and redux-saga

[...TODO]

Usage

Install the package via npm - npm install redux-saga-rxjs --save. Package exposes single middleware to be used in your Redux application. The middleware takes Sagas as its arguments.

import { createStore, applyMiddleware } from 'redux';
import sagaMiddleware from 'redux-saga-rxjs';

// Example of simplest saga
// Whenever action FOO kicks-in, Saga will dispatch
// BAR action
const saga = iterable => iterable
  .filter(({ action, state }) => action.type === 'FOO')
  .map(() => ({ type: 'BAR' }));

const storeFactory = applyMiddleware(
  sagaMiddleware(saga, sagaFoo...) // You can provide more than one Saga here
)(createStore);

// Very simple identity reducer which is not doing anything
const identityReducer = appState => appState;

// Use the store as you are used to in traditional Redux application
const store = storeFactory(identityReducer);

Development

  npm install
  npm run test:watch

More Repositories

1

node-pg-migrate

Node.js database migration management for PostgreSQL
TypeScript
1,286
star
2

prism

React / Redux action composition made simple http://salsita.github.io/prism/
TypeScript
496
star
3

chrome-extension-skeleton

Minimal skeleton for Chrome extension
JavaScript
470
star
4

redux-side-effects

Redux toolset for keeping all the side effects inside your reducers while maintaining their purity.
JavaScript
182
star
5

jq-clipthru

CoffeeScript
88
star
6

chrome-extension-skeleton-ng

JavaScript
68
star
7

flux-boilerplate

Very simple flux boilerplate with stateless stores and effect reducers
JavaScript
58
star
8

geo-tree

High performance library for geographical map-related operations
JavaScript
52
star
9

inStyle

Modify the current selector &: https://instyle-css.salsitasoft.com
CSS
47
star
10

flask-raml

Flask-RAML (REST API Markup Language) API server with parameter conversion, response encoding, and examples.
Python
47
star
11

passthruapp

Updated version of Igor Tandetnik's PassthroughAPP
C
26
star
12

redux-elm-skeleton

Skeleton project for quick start with redux-elm
JavaScript
18
star
13

web-api-proxy

Proxy any request to a remote web API to avoid embedding secret keys in your client-side app
JavaScript
15
star
14

go-jira

JIRA REST API client library for Go (Golang)
Go
15
star
15

jquery-ui-scalebreaker

does cool stuff
JavaScript
14
star
16

generator-salsa

JavaScript
12
star
17

go-pivotaltracker

Pivotal Tracker API client for Go (Golang)
Go
11
star
18

backbone-schema

JavaScript
11
star
19

shishito

Python module for selenium webdriver test execution
Python
10
star
20

redux-form-actions

JavaScript
10
star
21

dripping-bucket

Library to calculate delays for operations running against rate-limited services
JavaScript
10
star
22

ng2-if-media

TypeScript
9
star
23

grunt-package-minifier

Minimizes space taken by node modules and their dependencies.
JavaScript
9
star
24

Magpie

CommonJS partial implementation for Windows Active Script (JScript)
C++
8
star
25

scalable-frontend-with-cyclejs

Scalable frontend with Cycle.js
JavaScript
7
star
26

flask-config

Flask configuration class.
Python
7
star
27

spicy-hooks

TypeScript
7
star
28

postcss-inrule

https://instyle-css.salsitasoft.com/
JavaScript
7
star
29

react-beacon

Onboarding tooltips for web apps using Slack-like beacons
JavaScript
7
star
30

grunt-userev

JavaScript
6
star
31

RestQ

Declarative way to get data from RESTful APIs.
CoffeeScript
6
star
32

browser-require

Implementation of CommonJS require() function for use in client-side environments where synchronous loading is appropriate (e.g. browser extension).
JavaScript
6
star
33

react-training

react training app
TypeScript
5
star
34

jenkins-docker-skeleton

Test your app in Jenkins using Docker
Shell
5
star
35

mastermind

Mastermind (board game) with all the cool stuff packed in, namely redux-saga, normalizr, reselect and react-router.
JavaScript
5
star
36

todo2issue

CLI tool to synchronize in-code TODOs to GitHub issues
TypeScript
4
star
37

bunny-migrate

CLI tool for managing RabbitMQ schema instances
JavaScript
4
star
38

xml2js-schema

Extends node-xml2js with schema validation support based on JSON schema
JavaScript
4
star
39

react-devstack

An ultimate development stack built on top of React, Redux, Router5, Redux-Saga, React-Helmet
JavaScript
4
star
40

pyraml

RAML (REST API Markup Language) enhanced loader, parameter converter, and API wrapper.
Python
4
star
41

backbone-xml

JavaScript
4
star
42

go-sprintly

Sprintly API client for Go (Golang)
Go
3
star
43

winunit

Ready to use WinUnit for C++ unit testing
C++
3
star
44

foosball-rating

Keeps track of foosball player's rating based on augmented chess elo rating
JavaScript
3
star
45

flask-ecstatic

Serves static files with optional directory index.
Python
3
star
46

Serrano

Simple web data extraction language.
JavaScript
3
star
47

nodejs-training

backed part for our frontend (react/angular) trainings
JavaScript
3
star
48

mastermind-server

JavaScript
3
star
49

redux-ducks

Redux toolset for isolating state as defined by ducks-modular-redux proposal.
JavaScript
3
star
50

nodejs-modules

reusable modules for node.js backed servers / services
JavaScript
2
star
51

versionserver

A servlet providing auto-incremented build numbers for projects being built on Jenkins or other environments.
Python
2
star
52

angular-training

angular training app
TypeScript
2
star
53

iesetuphelper

Shared library containing helper functions for Windows setup packages.
C++
2
star
54

siros-postgres

New generation of HW / budget tracking system.
JavaScript
2
star
55

redux-prism

new name for redux-elm
2
star
56

grunt-md-templator

Take a Grunt template, add a hint of Markdown, shake and serve as HTML.
CoffeeScript
2
star
57

node_installer

Shell
2
star
58

crx-utils

Utilities for working with Chrome extension (.crx) files.
JavaScript
2
star
59

flask-run

Flask-based web application runner.
Python
2
star
60

salsita-dancing-santa

Celebrate the holiday season with Santa dancing to the dulcet tones of Wham!
CSS
2
star
61

pwa-cordova-meetup-app

Demo application for Salsita Meet Up May 14, 2019
Java
2
star
62

pydataloader

Extensible JSON/YAML/RAML/... data loader for Python.
Python
2
star
63

express-angular-skeleton

Skeleton for our new Express & Angular projects.
CSS
1
star
64

ng-modules

reusable modules for angular apps
TypeScript
1
star
65

wrapperElement

CSS
1
star
66

angular2-migration

JavaScript
1
star
67

jquery-viewable

lightweight jQuery extension that determines if a specific element is visible on a page
JavaScript
1
star
68

runtime-config

JavaScript
1
star
69

cci-pingu

Periodically check for new builds (artifacts) on CircleCI and install them in turn.
JavaScript
1
star
70

react-modules

reusable modules for react / redux apps
1
star
71

flask-serverinfo

Flask server info view for inspecting server app and user requests.
Python
1
star
72

angularjs-cayenne

JavaScript
1
star
73

metros-powerup

Card pointing power up for Trello
JavaScript
1
star
74

salsita.github.io

HTML
1
star
75

left-pad-service

Demo application for Salsita presentation on 2016/11/24
JavaScript
1
star
76

cordova-node-api

JavaScript
1
star
77

shishito-sample-project

Sample project for Salsa WebQA library
Python
1
star
78

bloggo

Poet & Angular based blagging app.
JavaScript
1
star