redux-ruleset
Redux-Ruleset is a zero-dependency redux-middleware that manages the business logic of your redux state. It is the counterpart of a controller in a classical MVC architecture. That includes managing side-effects and data-flows. With small independent and easy to refactor rules you can model any data-flow you want.
Getting Started
Install
$ npm install --save redux-ruleset
or
$ yarn add redux-ruleset
and when you create your redux store, add the middleware:
import {applyMiddleware, compose, createStore} from 'redux'
import {middleware as ruleMiddleware} from 'redux-ruleset'
const middlewares = [ruleMiddleware]
const enhancers = []
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middlewares),
...enhancers
)
)
Documentation
Usage Example
class App extends React.Component {
...
componentDidMount(){
const {dispatch} = this.props
dispatch({type: 'FETCH_USER_REQUEST'})
}
...
}
When the App mounts we want to fetch the current user, so we dispatch an action FETCH_USER_REQUEST
. A rule can listen to the action and fetch the user data
import {addRule} from 'redux-ruleset'
/*
adding a rule is absolutly boilerplate free. just call `addRule` anywhere in your application
and the rule will be added
*/
addRule({
id: 'FETCH_USER', // name of your rule (unique)
target: 'FETCH_USER_REQUEST', // the action type the rule listens to
concurrency: 'FIRST', // as long as api.fetchUser did not resolve, the rule won't be executed again
consequence: () => api.fetchUser().then(
user => ({ type: 'FETCH_USER_SUCCESS', payload: user }), // dispatch success
error => ({ type: 'FETCHUSER_FAILURE', payload: error }) // dispatch error
)
})
You can also define the the exact time, when the rule should be active. Let's say we want to develop a game. Everytime the user clicks a button, a PING
action is dispatched and your rule responds with a PONG
. But this should only happen, when the game has started:
addRule({
id: 'PING_PONG',
target: 'PING',
addWhen: function* (next){
yield next('START_GAME') // wait for next action with type START_GAME
return 'ADD_RULE' // set the rule to active
},
addUntil: function* (next){
yield next('STOP_GAME') // wait for next action with type STOP_GAME
return 'RECREATE_RULE' // remove the rule and reapply addWhen
},
consequence: () => ({ type: 'PONG' }) // dispatch a PONG for every PING
})
dispatch({type: 'PING'}) // nothing happens
dispatch({type: 'START_GAME'})
dispatch({type: 'PING'}) // => dispatch({type: 'PONG'})
dispatch({type: 'STOP_GAME'})
dispatch({type: 'PING'}) // nothing happens