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-action-handlers

v1.0.1

Published

Redux actionHandlers object

Downloads

1

Readme

ReduxActionHandlers

ActionHandlers library to improve the Redux development experience

Node version

https://nodei.co/npm/redux-action-handlers.png?downloads=true&downloadRank=true&stars=true

contributions welcome

How to use this:

  • Put all Your reducer cases into functions, and put them in the action file

src/actions/downloadSomething.js

export function downloadSomethingRequest_actionHandler(state, action) {
  return {
    ...state,
    registration: action.payload,
    isFetching: true,
    data: {}
  }
}
  • Add them to actionHandlers (for reducer "something", map "DOWNLOAD_SOMETHING_REQUEST" action type to "downloadSomethingRequest_actionHandler" function)
actionHandlers.add('something', DOWNLOAD_SOMETHING_REQUEST, downloadSomethingRequest_actionHandler)
  • Add handling to reducer
const reducer = (state = initialState, action = {}) => {
  return actionHandlers.runReducer('something', state, action);
}

Benefits of using this library:

  • One file describing all the functionality of an action. Instead of splitting the action creator logic and reducer logic to two separate files, one concern is in one file.
  • Huge reducers are no longer a problem. Instead of having all the reducer handling code in one huge switch...case block, each action is in it's own function which makes it cleaner.
  • When using combineReducers, You create shards. Those shards are enforced in actionHandlers. This means that You will never have a polluted root reducer namespace

Compatibility:

  • This library has no dependencies
  • It does not force the ActionHandlers on Your whole project. You can still use both classic reducer switch...case and ActionHandlers.
  • It does not pollute the redux state with functions.
  • It's a clean library - it supports all use cases.
  • Supports nested combineReducers
  • Supports many-to-many relationships between actions and reducer cases.
  • It's tiny. All it takes is 31 lines of code.
  • Testing is unchanged, but can be optimized. By testing the reducer functions directly, tests can become much clearer, but this library doesn't force it. You can test the whole reducer.

ReduxActionHandlers reason to exist

To create a single action and a reducer, you have to create multiple files and put a part of the implementation in each of them:

src/actions/downloadSomething_types.js

export const DOWNLOAD_SOMETHING_REQUEST = '@request/DOWNLOAD_SOMETHING_REQUEST'
export const DOWNLOAD_SOMETHING_SUCCESS = '@request/DOWNLOAD_SOMETHING_SUCCESS'
export const DOWNLOAD_SOMETHING_FAILURE = '@request/DOWNLOAD_SOMETHING_FAILURE'

src/actions/downloadSomething.js

import {
  DOWNLOAD_SOMETHING_REQUEST,
  DOWNLOAD_SOMETHING_SUCCESS,
  DOWNLOAD_SOMETHING_FAILURE
} from 'src/actions/downloadSomething_types.js'

export default function downloadSomethingAction() {
  return (dispatch) => {
    dispatch({
      type: DOWNLOAD_SOMETHING_REQUEST
    })

    return api.downloadSomething()
      .then(
        json => dispatch({
          type: DOWNLOAD_SOMETHING_SUCCESS,
          payload: json
        }),
        exception => {
          dispatch({
            type: DOWNLOAD_SOMETHING_FAILURE
          })
          dispatch(notifyError(exception.message))
        }
      )
  }
}

src/reducers/downloadReducer.js

import {
  DOWNLOAD_SOMETHING_REQUEST,
  DOWNLOAD_SOMETHING_SUCCESS,
  DOWNLOAD_SOMETHING_FAILURE
} from 'actions/downloadSomething_types.js'

export const initialState = {
  isFetching: false,
  data: {},
  registration: null,
  visible: false
};

const reducer = (state = initialState, action = {}) => {

  if (!action.type) return state

  switch (action.type) {

    case DOWNLOAD_SOMETHING_REQUEST:
      return {
        ...state,
        registration: action.payload,
        isFetching: true,
        data: {}
      }

    case DOWNLOAD_SOMETHING_SUCCESS:
      return {
        ...state,
        data: action.payload,
        isFetching: false
      }

    case DOWNLOAD_SOMETHING_FAILURE:
      return {
        ...state,
        data: {},
        isFetching: false
      }

    default:
      return state

  }
}

export default reducer

This single concern, single chunk of functionality is spread over three separate files.

It makes it difficult to reason about and debug. The reducer construction is also an issue because it very easily can grow into a monster like this (and this has a potential to be much bigger):

import {
  OPEN_INFO_PANEL,
  CLOSE_INFO_PANEL,
  READ_INFO_REQUEST,
  READ_INFO_SUCCESS,
  READ_INFO_FAILURE,
  UPDATE_TIMES_REQUEST,
  UPDATE_TIMES_SUCCESS,
  UPDATE_TIMES_FAILURE,
  UPDATE_POSITION_STAND_REQUEST,
  UPDATE_POSITION_STAND_SUCCESS,
  UPDATE_POSITION_STAND_FAILURE
} from 'features/info/actions'


export const defaultState = {
  data: {},
  flightId: null,
  isFetching: false,
  isUpdating: false,
  visible: false
}

export default (state = defaultState, action = {}) => {

  if (!action.type) return state

  switch(action.type) {

    case OPEN_INFO_PANEL:
      return {
        ...state,
        flightId: action.payload,
        isFetching: false,
        visible: true
      }

    case CLOSE_INFO_PANEL:
      return {
        ...state,
        flightId: null,
        isFetching: false,
        visible: false
      }

    case READ_INFO_REQUEST:
      return {
        ...state,
        data: {},
        flightId: action.payload,
        isFetching: true
      }

    case READ_INFO_SUCCESS:
      return {
        ...state,
        data: action.payload,
        isFetching: false
      }

    case READ_INFO_FAILURE:
      return {
        ...state,
        data: {},
        isFetching: false
      }

    case UPDATE_TIMES_SUCCESS:
      return {
        ...state,
        isUpdating: false,
        data: { ...state.data,
          times: {...state.data.times,
            actual: {...action.payload.actualTimes}
          }
        }
      }

    case UPDATE_POSITION_STAND_SUCCESS:
      return {
        ...state,
        isUpdating: false,
        data: {
          ...state.data,
          gatesInfo: {
            ...action.payload
          }
        }
      }

    case UPDATE_TIMES_REQUEST:
      return {
        ...state,
        isUpdating: true
      }

    case UPDATE_POSITION_STAND_REQUEST:
      return {
        ...state,
        isUpdating: true
      }

    case UPDATE_TIMES_FAILURE:
      return {
        ...state,
        isUpdating: false
      }

    case UPDATE_POSITION_STAND_FAILURE:
      return {
        ...state,
        isUpdating: false
      }

    default:
      return state
  }
}

Solution:

ReduxActionHandlers!

Idea:

Put all the reducer cases into functions like so:

Before:

case DOWNLOAD_SOMETHING_REQUEST:
  return {
    ...state,
    registration: action.payload,
    isFetching: true,
    data: {}
  }

After:

function downloadSomethingRequest_actionHandler(state, action) {
  return {
    ...state,
    registration: action.payload,
    isFetching: true,
    data: {}
  }
}

Then, instead of attaching actions externally ( from reducer ) we would attach actions to a reducer like so:

actionHandlers.add('something', DOWNLOAD_SOMETHING_REQUEST, downloadSomethingRequest_actionHandler)

Resulting files would be as follows:

src/actions/downloadSomething.js

import actionHandlers from 'redux-action-handlers'

const DOWNLOAD_SOMETHING_REQUEST = '@request/DOWNLOAD_SOMETHING_REQUEST'
const DOWNLOAD_SOMETHING_SUCCESS = '@request/DOWNLOAD_SOMETHING_SUCCESS'
const DOWNLOAD_SOMETHING_FAILURE = '@request/DOWNLOAD_SOMETHING_FAILURE'

export function downloadSomethingRequest_actionHandler(state, action) {
  return {
    ...state,
    registration: action.payload,
    isFetching: true,
    data: {}
  }
}

export function downloadSomethingSuccess_actionHandler(state, action) {
  return {
    ...state,
    data: action.payload,
    isFetching: false
  }
}

export function downloadSomethingFailure_actionHandler(state, action) {
  return {
    ...state,
    data: {},
    isFetching: false
  }
}

export default function downloadSomethingAction() {
  return (dispatch) => {
    dispatch({
      type: DOWNLOAD_SOMETHING_REQUEST
    })

    return api.downloadSomething()
      .then(
        json => dispatch({
          type: DOWNLOAD_SOMETHING_SUCCESS,
          payload: json
        }),
        exception => {
          dispatch({
            type: DOWNLOAD_SOMETHING_FAILURE
          })
          dispatch(notifyError(exception.message))
        }
      )
  }
}

actionHandlers.add('something', DOWNLOAD_SOMETHING_REQUEST, downloadSomethingRequest_actionHandler)
actionHandlers.add('something', DOWNLOAD_SOMETHING_SUCCESS, downloadSomethingSuccess_actionHandler)
actionHandlers.add('something', DOWNLOAD_SOMETHING_FAILURE, downloadSomethingFailure_actionHandler)

src/reducers/downloadSomethingReducer.js

import actionHandlers from 'redux-action-handlers'

export const initialState = {
  isFetching: false,
  data: {},
  registration: null,
  visible: false
};

const reducer = (state = initialState, action = {}) => {
  return actionHandlers.runReducer('something', state, action);
}

export default reducer