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-motive

v0.5.0

Published

Simplify writing action creators, reducers and effects - without breaking redux.

Downloads

40

Readme

Redux Motive stability

size NPM Travis Codecov

Simplify writing action creators, reducers and effects - without breaking redux.

const { reducer, ...actionCreators } = ReduxMotive({
  config: {},
  sync: {
    // Sync function, combines Action Creator and Reducer
    addTodo (state, todo) {
      return assign({}, state, { todos: [ ...state.todos, todo ] })
    },
  },
  async: {
    // Async function, combines Action Creator and Effect
    async createTodo (motive, text, isDone) {
      const todo = await api('/todo', {text, isDone})
      motive.addTodo(todo)
    }
  },
})

Install

yarn add redux-motive

Requirements

Add redux-thunk to your store's middleware. See redux-thunk docs for more details.

yarn add redux-thunk
import thunk from 'redux-thunk'
const store = createStore(reducers, applyMiddleware(thunk))

Preamble

In UI development, our motive's for using redux are predictable.

  1. Reduce an Action to change the state now, to rerender the UI soon.
  2. Reduce the lifecycle of side effects, from an Action, to change state over time, to rerender the UI as the side effects progress.

Redux is great for splitting data-flow concerns into small concepts, but it can introduce indirection to a developers code, and at times this becomes the source of errors.

Motive removes indirection, by combining the purpose of a data-flow function to be both an Action Creator and a Reducer, or an Action Creator and an Effect.

Comparison

Generate action creators and a reducer with Motive.

const { reducer, ...actionCreators } = ReduxMotive({
  sync: {
    // Sync function, combines Action Creator and Reducer
    addTodo (state, todo) {
      return assign({}, state, { todos: [ ...state.todos, todo ] })
    },
  },
  async: {
    // Async function, combines Action Creator and Effect
    async createTodo (motive, text, isDone) {
      const todo = await api('/todo', {text, isDone})
      motive.addTodo(todo)
    }
  },
})

Write action types, action creators and reducers with common redux boilerplate.

const ADD_TODO = '@@MOTIVE/ADD_TODO'
const CREATE_TODO_START = '@@MOTIVE/CREATE_TODO_START'
const CREATE_TODO_END = '@@MOTIVE/CREATE_TODO_END'
const CREATE_TODO_ERROR = '@@MOTIVE/CREATE_TODO_ERROR'

const reducer = (state, action) => {
  switch (action.type) {
    case ADD_TODO:
      return assign({}, state, { todos: [ ...state.todos, todo ] })
    case CREATE_TODO_START:
      return assign({}, state, { progressing: true })
    case CREATE_TODO_END:
      return assign({}, state, { progressing: false })
    case CREATE_TODO_ERROR:
      return assign({}, state, { error: action.payload, progressing: false })
  }
}

const actionCreators = {
  addTodo (todo) {
    return { type: ADD_TODO, payload: { todo } }
  },

  createTodo (text, isDone) {
    return (dispatch) => {
      dispatch({ type: CREATE_TODO_START })
      api('/todo', {text, isDone})
        .then(todo => {
          dispatch(actionCreators.addTodo(todo))
          dispatch({ type: CREATE_TODO_END })
        })
        .catch(err => {
          dispatch({ type: CREATE_TODO_ERROR, payload: err })
        })
    }
  }
}

Summary

Inferring common redux patterns into ReduxMotive allows for less coding.

  • Action Creators often pass their params to Reducers in the Action; ReduxMotive always does behind the scenes.
  • The progress of an effect's lifecycle in ReduxMotive is reduced to state at common stages: start, end or error.
  • Dispatching actions from the end of effects is guaranteed; ReduxMotive provides dispatch-bound Action Creators in an effect's first parameter.

API

ReduxMotive( { config, sync, async } )

The returned object can be used to provide a reducer to the Redux.

Additionally, every function configured for sync and async are accessible as dispatchable Action Creators.

const motive = ReduxMotive({
  config: {}
  sync: {
    todo () {},
  },
  async: {
    async fetchTodo () {}
  }
});

console.log(motive);
// {
//   reducer,               Reducer function, wrapping all configured sync fns
//   todo,                  An Action Creator generated from sync.todo
//   fetchTodo              An Action Creator generated from async.fetchTodo
// }

Configuring

Initial state, default handlers for state/end/error, and optional prefix for action types.


ReduxMotive({
  // Default config values
  config: {
    prefix: '',
    initialState: {},
    handlers: {
      start: (state) => assign({}, state, { progressing: true }),
      end: (state) => assign({}, state, { progressing: false }),
      error: (state, error) => assign({}, state, { progressing: false, error })
    },
  }
})

A collection of functions that combine the principles of an Action Creator and a Reducer.

They should:

  1. Always return new state
  2. Should not call any "side effects"
const { todo } = ReduxMotive({
  sync: {
    todo (state, isDone) {
      return { ...state, isDone }
    }
  }
})

dispatch( todo(true) )

Combination of an Action Creator and an Effect.

Function that is given a motive Object and any additional arguments from the generated Action Creator.

Expected to dispatch new Actions from invoke side effects (like server API calls).

Should return a Promise. The async function keyword can be used.

motive Object

  • dispatch
  • getState
  • Action Creators returned by ReduxMotive, bound to dispatch
ReduxMotive({
  // ...

  async: {
    async fetchTodo (motive) {
      const todo = await api();
      motive.todo(todo.isDone)
    }
  }
})

Lifecycles for an Async Function

Refer to the Comparison for when 'lifecycle' stages are actioned and reduced.

The stages can be overridden:

  • In the config
  • Per (asynchronous) function
ReduxMotive({
  config: {
    handlers: { /* ... */ }
  },

  async: {
    fetchTodo: {
      handlers: {
        start (state) { /* ... */ },
        end (state) { /* ... */ },
        error (state) { /* ... */ }
      },
      async effect (motive) {
        const todo = await api();
        motive.todo(todo.isDone)
      }
    }
  }
})

Action Types

Action types for each Action Creators are available as properties, which is useful when attempting to match the types in a explicit way.

console.log(motive.todo.ACTION_TYPE)
// @@MOTIVE/<PREFIX>/TODO_SYNC

console.log(motive.fetchTodo.ACTION_TYPE_START)
// @@MOTIVE/<PREFIX>/SYNC_TODO_START
console.log(motive.fetchTodo.ACTION_TYPE_END)
// @@MOTIVE/<PREFIX>/SYNC_TODO_END
console.log(motive.fetchTodo.ACTION_TYPE_ERROR)
// @@MOTIVE/<PREFIX>/SYNC_TODO_ERROR

You don't need to use these if you're dispatching the generated Action Creators.

Alternatives & inspirations

Library | Description --- | --- redux-schemas | Similar redux util library, making different API choices, but with more utility. freactal | Unidirection store for React, with a concise api for async actions and selectors.

License

Licensed under the MIT License, Copyright © 2017-present Lochlan Bunn.