Rules API for Redux applications.
Switch branches/tags
Nothing to show
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
.eslintrc
.gitignore
.npmignore
.travis.yml
LICENSE.md
README.md
package.json

README.md

Rules API for Redux

Make your code more easy to reason about with a more natural language for your logic, using rules fired by actions and reacting to a given set of facts.

Based on the forward-chaining rules in Clojure called Clara and a discussion from an issue of Choko core.

Build Status

Where Redux gets in?

[ACTION] => ([RULES] -> [CONDITIONS] -> [REACTIONS]) => [STATE]

Obs.: Reactions are dispatched actions / side-effects.

Redux gives us the tools to build functional rules systems using it's middleware API. So basically each rule behaves like a Redux's middleware.

Vanilla Redux:

import { createStore, applyMiddleware} from 'redux'
import { flashMessage, FLASH_MESSAGE } from 'app/modules/messages/actions'

const LOGIN_SUCCESS = 'user/login/SUCCESS'

const welcomeAuthUserMiddleware = store => next => action => {
  switch (action.type) {
    case LOGIN_SUCCESS:
      if (action.payload.user.roles.some(role => role === 'authenticated')) {
        const { name, lastLogin } = action.payload.user
        if (!!lastLogin) {
          store.dispatch(flashMessage('Good to see you ' + name + '!'))
        }
      }
      break;
  }
  return next(action)
}

const reducer = (state, action) => {
  if (action.type === LOGIN_SUCCESS) {
    return { ...state, user: action.payload.user }
  }

  if (action.type === FLASH_MESSAGE) {
    return { ...state, message: action.payload }
  }
}

const store = createStore(reducer, applyMiddleware(welcomeAuthUserMiddleware))

store.dispatch({
  type: LOGIN_SUCCESS,
  payload: { user: { name: 'Manolo', roles: ['authenticated'] } }
})

console.log(getState().message) // Good to see you Manolo!

Using Redux Rules:

import { createStore, applyMiddleware } from 'redux'
import combineRules, { every } from 'redux-rules'
import { flashMessage, FLASH_MESSAGE } from 'app/modules/messages/actions'

const LOGIN_SUCCESS = 'user/login/SUCCESS'

const isAuthUser = ({ action }) => action.payload
  .user.roles.some(role => role === 'authenticated')
const isComingBackUser = ({ action }) => !!action.payload.user.lastLogin

const welcomeAuthUserMessageRule = {
  type: 'messages/user/login/SUCCESS',
  actionTypes: [LOGIN_SUCCESS],
  condition: every([
    isAuthUser, isComingBackUser
  ]),
  reaction: store => next => action => {
    const { name } = action.payload.user
    store.dispatch(flashMessage('Good to see you ' + name + '!'))
    return next(action)
  }
}

const rulesMiddleware = combineRules({
  rules: [welcomeAuthUserMessageRule]
})

const reducer = (state, action) => {
  if (action.type === LOGIN_SUCCESS) {
    return { ...state, user: action.payload.user }
  }

  if (action.type === FLASH_MESSAGE) {
    return { ...state, message: action.payload }
  }
}

const store = createStore(reducer, applyMiddleware(rulesMiddleware))

store.dispatch({
  type: LOGIN_SUCCESS,
  payload: { user: { name: 'Manolo', roles: ['authenticated'] } }
})

console.log(getState().message) // Good to see you Manolo!

Usage:

LICENSE

MIT