1.0.0 • Published 6 years ago

redux-guard v1.0.0

Weekly downloads
4
License
MIT
Repository
github
Last release
6 years ago

redux-guard

A middleware that turns Redux into a state machine, enforcing how your actions are sequenced.

Redux Guard protects future you from present you.

Example

Say you're signing in a user:

// Reducer

const initialState = {
    __currentState: 'UNAUTHENTICATED',
    message: 'Please sign in to continue.'
}

function reducer(state = initialState, action) {
    switch (action.type) {
        case 'LOG_OUT':
            return {
                __currentState: 'UNAUTHENTICATED',
                message: 'Please sign in to continue.'
            }
        case 'LOG_IN':
            return {
                __currentState: 'AUTHENTICATING',
                message: 'You are now signed in.'
            }
        default:
            return state
    }
}

And you are dispatching the following actions in succession:

store.dispatch({ type: 'LOG_IN' }) // Great! Let's log you in.
store.dispatch({ type: 'LOG_OUT' }) // Okay, now you're logged out.
store.dispatch({ type: 'LOG_OUT' }) // Huh? Why you logging out again?

It doesn't make sense for a user to log out a second time after they are already logged out.

Redux Guard provides a means to protect against scenarios like this. You configure a set of constraints up front, and it enforces them... throwing an error if anything is amiss:

import { createStore, applyMiddleware } from 'redux'
import { createGuardMiddleware } from 'redux-guard'
import rootReducer from './reducers/index'

// Write your config for the redux-guard middleware.
const config = {
    guards: [
        {
            // Point to where you're tracking current status.
            getCurrentState: state => state.__currentState,
            constraints: {
                // If we're authenticated, we can only log out.
                'AUTHENTICATED': ['LOG_OUT'],
                // If we're unauthenticated, we can only log in.
                'UNAUTHENTICATED': ['LOG_IN']
            }
        }
    ]
}

// Apply the middleware, passing in the config.
const store = createStore(
    rootReducer,
    applyMiddleware(
        createGuardMiddleware(config),
        ...otherMiddleware
    )
)

Because this middleware is designed to stop improperly dispatched actions before they make undesired changes to your app, it is recommended that you pass this to applyMiddleware first.

Now when you try dispatching the same actions, Guard will stop you:

store.dispatch({ type: 'LOG_IN' }) // ✅
store.dispatch({ type: 'LOG_OUT' }) // ✅
store.dispatch({ type: 'LOG_OUT' }) // 🚫 Throws an error.