Connects Rosmaro, Redux and Redux-Saga.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src
.babelrc
.gitignore
.npmignore
LICENSE.txt
README.md
package-lock.json
package.json

README.md

Rosmaro-Redux

Connects Rosmaro, Redux and Redux-saga.

Uses Rosmaro to implement state-related functions, like the reducer.

Uses Redux to build a stateful object - the store.

Uses Redux-saga to handle side-effects.

How to set up rosmaro-redux

First, the package needs to be installed:

npm i rosmaro-redux

Then, all the dependencies need to be imported into the same file, where the Redux store is built:

import {makeReducer, effectDispatcher} from 'rosmaro-redux';

Let's assume that rosmaroModel is a Rosmaro model, that is a ({state, action}) => ({state, result}) function. Then the reducer is built in the following way:

import {makeReducer, effectDispatcher} from 'rosmaro-redux';

const reducer = makeReducer(rosmaroModel);

The store requires two middlewares:

import {makeReducer, effectDispatcher} from 'rosmaro-redux';
import createSagaMiddleware from 'redux-saga';
import {createStore, applyMiddleware} from 'redux';

const reducer = makeReducer(rosmaroModel);
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
  reducer,
  applyMiddleware(effectDispatcher, sagaMiddleware)
);

Assuming that saga is our saga, we run in in the following way:

import {makeReducer, effectDispatcher} from 'rosmaro-redux';
import createSagaMiddleware from 'redux-saga';
import {createStore, applyMiddleware} from 'redux';

const reducer = makeReducer(rosmaroModel);
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
  reducer,
  applyMiddleware(effectDispatcher, sagaMiddleware)
);
sagaMiddleware.run(saga);

Using Rosmaro Redux with Redux DevTools

Due to effectDispatcher, which is a required middleware, we need to compose enhancers.

To make things easier, we can use the the redux-devtools-extension package:

npm i redux-devtools-extension
import {makeReducer, effectDispatcher} from 'rosmaro-redux';
import createSagaMiddleware from 'redux-saga';
import {createStore, applyMiddleware} from 'redux';
import {composeWithDevTools} from 'redux-devtools-extension';

const reducer = makeReducer(rosmaroModel);
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
  reducer,
  composeWithDevTools(
    applyMiddleware(effectDispatcher, sagaMiddleware)
  )
);
sagaMiddleware.run(saga);

For more information, please check the official documentation of Redux DevTools out.

How to write Rosmaro handlers

Every Rosmaro handler is supposed to return a result in the shape of {data, effect}. While the data may be an arbitrary value, the effect needs to be either an action recognized as an effect by the saga or an array of effects. This is a simple, valid result value:

{data: undefined, effect: {type: 'INCREMENT', value: 42}}

This is correct as well:

{
  data: undefined, 
  effect: [
    {type: 'INCREMENT', value: 1},
    {type: 'INCREMENT', value: 2},
    [
      {type: 'INCREMENT', value: 3},
      {type: 'INCREMENT', value: 4},
    ],
    {type: 'INCREMENT', value: 5}
  ]
}

You may like the rosmaro-binding-utils package. It makes returning a result in the shape of {data, effect} easy.

Redux-Saga - reacting only to effects

This package exports a tiny predicate - isEffect.

import {isEffect} from 'rosmaro-redux';

This functions returns true when it's given an action which was returned as an effect. That way we can distinguish actions simply dispatched to the store from actions returned as effects.

Here's an example of taking only those INCREMENT actions which are effects:

import {isEffect} from 'rosmaro-redux';
// ...
yield takeEvery(action => isEffect(action) && action.type === 'INCREMENT', increment);

It can be even shorter with matchEffect:

import {matchEffect} from 'rosmaro-redux';
// ...
yield takeEvery(matchEffect('INCREMENT'), increment);

Sagas

dispatchActionSaga

This saga looks for {type: 'DISPATCH', action} effects and dispatches the action.

import {dispatchActionSaga} from 'rosmaro-redux';

// ...
const saga = function* () {
  yield all([dispatchActionSaga()]);
};

sagaMiddleware.run(saga);

Returning an effect like this:

effect: {
  type: "DISPATCH",
  action: {type: "ACTUALLY_INCREMENT"}
}

will make this saga dispatch:

{type: "ACTUALLY_INCREMENT"}