redux-saga-factory
v1.1.0
Published
> Next generation sagas using classes and decorators
Downloads
4
Readme
redux-saga-factory 🏭
Next generation sagas using classes and decorators
Introduction
Sagas are great, but they don’t always fit nicely inside large scale projects since:
- They are nothing more than generator functions, so it is impossible to use them with dependency injection patterns.
- You often have to type repeated boilerplate only to wrap the actual action handler.
This library will allow you to:
- Create sagas using factory classes.
- Leverage decorators to yield common saga patterns (hence removing boilerplate).
Installation
yarn install redux-saga-factory reflect-metadata
Usage
1. Create Saga using class & decorators
import 'reflect-metadata'
import { take, SagaFactory } from 'redux-saga-factory'
export class KickassSaga extends SagaFactory {
@take("some-action")
*onSomeAction(action) {
const result = yield call(fetchSomething);
return result.data;
}
}
2. Use the saga
import { KickassSaga } from "./KickassSaga.ts";
// Setup the factory
const kickassSaga = new KickassSaga(/* dependencies? */);
// Get the sagas
const sagas = kickassSaga.getSagas();
// sagas === { onSomeAction: [Function: onSomeAction]}
// Create the store as usual
import { Action, createStore, applyMiddleware } from "redux";
import sagaMiddlewareFactory from "redux-saga";
const sagaMiddleware = sagaMiddlewareFactory();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
Object.values(saga).forEach(item => sagaMiddleware.run(item));
⚠️ You must
import "reflect-metadata"
polyfill and you should only import it once in the code.
Deeper look
Say you have this saga:
function* someSaga() {
while (true) {
const action = yield take("some_action");
yield call(sagaWorker, action);
}
}
function* sagaWorker(action) {
// do the magic
}
We can change it to this
class SomeSaga {
@take("some_action")
static *sagaWorker(payload) {
// do the magic
}
}
// and use it by...
const sagas = SagaFactory.fromMetadata(SomeSaga);
// sagas = { some_action_saga: [Function: sagaWorker] }
Or, if we want to add dependency injection:
class SomeSaga extends SagaFactory {
private readonly logger :ILogger;
constructor(logger:ILogger){
this.logger = logger;
}
@take("some_action")
*sagaWorker(payload) {
this.logger.debug("saga with di!")
// do the magic
}
}
// and use it by...
const factory = new SomeSaga(loggerInsatance);
const sagas = factory.getSagas()
// sagas = { some_action_saga: [Function: sagaWorker] }
You can also use the Factory to generate multiple sagas or use different effect patterns:
class MultiSaga extends SagaFactory {
@take("some_action")
*sagaWorker(payload) {
logger.debug("saga with di!")
// do the magic
}
@takeLatest("some_action")
*anotherSagaWorker(payload) {
// using takeLatest effect instead of take
}
}
Use with typescript-fsa:
This library is 100% compatible with typescript-fsa, which provides "type-safe experience with Flux actions with minimum boilerplate".
import { actionCreatorFactory } from 'typescript-fsa'
const actionCreator = actionCreatorFactory();
const fsaAction = actionCreator<{ foo: string }>("SUBMIT_ACTION");
class FsaSaga extends SagaFactory {
@take(fsaAction)
*sagaWorker(payload) {
// do the magic
}
}
Use with typescript-fsa-redux-saga
Another great library is typescript-fsa-redux-saga which easily wraps sagas with async actions. Resulting saga dispatches started action once started and done/failed upon finish.
This can be achieved automatically with redux-saga-factory
by passing the AsyncAction creator as an argument:
import { actionCreatorFactory } from 'typescript-fsa'
const actionCreator = actionCreatorFactory();
const fsaAction = actionCreator<{ foo: string }>("SUBMIT_ACTION");
const fsaAsyncAction = actionCreator.async<{ foo: string }, { bar: string}>("SUBMIT_ASYNC_ACTION");
class FsaSaga extends SagaFactory {
@take(fsaAction, fsaAsyncAction)
*sagaWorker(payload) {
// do the magic
}
}
// store.dispatch(fsaAction({foo: 'bar'}))
//
// Actions fired:
//
// 1. SUBMIT_ACTION
// 2. SUBMIT_ASYNC_ACTION_STARTED
// --- Here sagaWorker will be called ---
// 3. SUBMIT_ASYNC_ACTION_DONE
Use without class
TBA
Use with custom saga pattern
TBA