redux-sigma
is a library that allows implementation of state machines on top
of redux
and redux-saga
.
State machines implemented with redux-sigma
react to events dispatched via redux
,
and their state can be stored inside redux
using a dedicated reducer.
The aim of redux-sigma
is providing developers with a formal framework
that can be used when dealing with complex business flows inside front-end applications.
Being based on redux-saga
, redux-sigma
expects all your redux actions to follow
the FSA pattern.
redux-sigma
has extensive TypeScript support, and we recommend using it with TypeScript.
You can read what features redux-sigma
offers in the
docs,
or you can start by reading the quick start below.
If you want to look at a more detailed example,
check out the example folder.
$ yarn add redux-sigma
Assuming you are using yarn
.
redux-sigma
has redux
and redux-saga
as peer dependencies.
State machines in redux-sigma
must extend a generic StateMachine
class.
The simplest way to define a state machine is to extend the StateMachine
class,
and to define its abstract fields:
import { StateMachine } from 'redux-sigma';
class MyStateMachine extends StateMachine {
initialState = 'first_state';
name = 'my_state_machine';
spec = {
first_state: {
transitions: {
first_event: 'second_state',
},
},
second_state: {
transitions: {
second_event: 'first_state',
},
},
};
}
This state machine can be represented graphically as follows:
The spec
field is the actual specification of the state machine:
a high level description of what its states are, and how the state machine
goes from one state to another.
More on this in the docs.
The initialState
field indicates what will be the state of the state machine
when it first starts.
The name
field is what identifies state machines: for redux-sigma
,
two state machines cannot have the same name.
To use a state machine, you first need to instantiate it:
export const myStateMachine = new MyStateMachine();
Then, you must connect your state machine to redux
via redux-saga
.
redux-sigma
provides a stateMachineStarterSaga
utility to coordinate state machines startup
that integrates with your redux
store and your redux-saga
middleware.
import { createStore, applyMiddleware } from 'redux';
import { createSagaMiddleware } from 'redux-saga';
import { stateMachineStarterSaga } from 'redux-sigma';
import { rootReducer } from './root-reducer';
import { myStateMachine } from './my-state-machine';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(stateMachineStarterSaga, myStateMachine);
Having more than one state machine with the same name or two instances of the same state machine passed to
stateMachineStarterSaga
will crashredux-sigma
!
State machines can be started and stopped by dispatching actions to redux
:
store.dispatch(myStateMachine.start({}));
store.dispatch(myStateMachine.stop());
Multiple start
actions dispatched one after another have no effect on the state machine:
the state machine is started only once.
The same is true for stop
actions.
To restart a running state machine, dispatch a stop
action followed by a start
action.
To have the state of your state machines available inside your redux
store,
use the stateReducer
of the state machine:
import { combineReducers } from 'redux';
import { myStateMachine } from './my-state-machine';
const rootReducer = combineReducers({
my_state_machine: myStateMachine.stateReducer,
});
While the state machine is not running, its state will look like this:
console.log(store.getState().my_state_machine);
// { state: null }
Once the state machine starts running, its state will look like this:
store.dispatch(myStateMachine.start({}));
console.log(store.getState().my_state_machine);
// { state: 'first_state', context: {} }
The state and context of the state machines will be updated independently during the state machine lifetime, according to its specification.