npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-machine-middleware

v0.3.1

Published

A simple state machine as redux middleware

Downloads

9

Readme

redux-machine-middleware

Coverage Status

Quick start

npm install redux-machine-middleware

Option A

import {
  createMachineMiddleware
  machinesReducer,
  transitionTo
} from 'redux-machine-middleware'

const counterMachine = {/* see example below */}

const machineMiddleware = createMachineMiddleware(
  // add as many machines as you like
  { counter: couterMachine },
  // use strict mode when developing
  { strict: true }
)

applyMiddleware(/* other middleware */ machineMiddleware)

const rootReducer = combineReducers({
  // this must be named machines
  machines: machinesReducer,
  counter: counterReducer
})

// initialize your reducer with your initial state
store.dispatch(transitionTo('counter', 'INITITAL_STATE'))

Option B

import {
  createMachineMiddleware,
  decorateReducerWithMachine,
  transitionTo
} from 'redux-machine-middleware'

const counterMachine = {
  /* see example below */
}

const machineMiddleware = createMachineMiddleware({}, {strict: true})

applyMiddleware(/* other middleware */ machineMiddleware)

const machineDecorator = decorateReducerWithMachine('counter', counterMachine)
const counterReducer = (state, action) => {
  if (action.type === INC) {
    return {...state, number: state.number + 1}
  }
  return state
}

const rootReducer = combineReducers({
  // give your reducer and initialState to the decorator
  counter: machineDecorator(counterReducer, {number: 0}),
  ...otherReducers
})

// initialize your reducer with your initial state
store.dispatch(transitionTo('counter', 'INITITAL_STATE'))

Why

Read here to learn a bit about finite state machines. As your features grow in complexity, if you only think of your UI in terms of boolean flags and conditionals to order to transition from state to state, the complexity in your views will escalate. Using Redux alone doesn't prevent this. The idea here is to let your store do what its does best--hold your data and update when actions are dispatched, while your machine middleware controls your state transitions, ideally eliminating the need to fill your view with logic that does not really document your intention.

Examples

I have ported David Khourshid's example app for xstate (an excellent full-featured finite state machine lib). The code is under /example. Here is the live version and the original. I plan to do a questionnaire with tabs of new questions dependent on your last answers.

API

Middleware

createMachineMiddleware:: (machines: Machines, options: {strict: boolean}) -> store -> next -> action

Actions

transitionTo :: (machineName: string, stateName: string) -> {type: '@@TRANSITION_STATE', machineName, stateName}

Transitions can either occur automatically via an autoTransition or by using the transitionTo action creator.

Machine reducer

OPTION A - single machineReducer

machineReducer :: (state: {}, action: {type: string}) -> state

  • must be named machines
  • only updates on one action type: TRANSITION_MACHINE_STATE
  • listen for this same action in other reducers if you like

OPTION B - decorate your reducers with machine using

decorateReducerWithMachine :: (machineName: string, machine: Machine) -> (reducer: function, initialState: {}) -> (state: {}, action: {type: string}) -> state

  • the state of your reducer will be merged with the state of the named machine
  • machine identifier is a symbol to avoid conflicting with your properties

In theory you could mix options A & B, although this is not yet tested and would probably lead to confusing code.

Validation

In strict mode, errors will be thrown for:

  • missing machine
  • transitionTo called with invalid next state
  • invalid transition object properties (prefix special properties with underscore or dollar sign and they will be ignored)
  • invalid transition object value types
  • cond function which does not return a truthy or falsey value

State machine config

const machines = {
  foo: {
    // it is recommend your initialize the machine reducer with this value when loading the component/app
    default: 'IDLE',
    // option to pass only a slice of redux store to cond functions
    selector: ['foo'],
    states: [
      {
        name: 'RUNNING',
        autoTransitions: [
          {
            // cond takes state and the current action and returns a boolean
            // if cond passes, the `to` value (next state) for the this transition will be dispatched with transitionTo
            cond: (foo, action) => action.payload + foo.number > 10
            to: 'IDLE'
            // if this state is dispatching the following actions will be dispatched before and after
            // ONLY for this particular autoTransition if nested in the autoTransition
            // in this case hooks outside of the autoTransition will not be called
            before: ({getState, dispatch, action}) => launchMissle(),
            after: ({getState, dispatch, action}) => cleanUpTheMess()
          }
        ],
        // in strict mode an error will be thrown if the next state for this state is in this list
        validTransitions: ['IDLE']
      },
      {
        name: 'IDLE',
        autoTransitions: [
          {
            cond: foo => foo.number === 2,
            to: 'RUNNING'
          }
        ],
        // if 'IDLE' is the next state called with transtionTo, 'before' or 'after' hooks will be called
        // It is IMPORTANT to notice that this is different logic that for autoTransitions
        // the hooks there happen round the departing state and here around the arriving state
        after: ({getState, dispatch, action}) => announceShutdown(action)
      },
      validTransitions: ['RUNNING']
    ]
  }
}

Selectors

Option A

getMachineState :: (name: string) -> (state: StoreState) -> string

Use this one if you use the single default reducer with the name machines.

The first string parameter is the name give to the machine.

Option B

getMachineStateFromDecorated :: (name: string) -> (state: StoreState) -> string

Use this one if you use the decorate your own reducer with decorateReducerWithMachine.

The first string parameter is the name given to the reducer.