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

v3.0.3

Published

Action enhancer middleware for redux.

Downloads

269

Readme

Redux Action Enhancers

Dependency injection for redux actions. Enhance your actions with values from the store. Inspired by connected components in react-redux.

npm install --save redux-action-enhancer
or
yarn add redux-action-enhancer

Usage

To start using redux action enhancers, add the middleware to your store.

import createActionEnhancerMiddleware from 'redux-action-enhancer';
import {createStore, applyMiddleware} from 'redux';
import enhancers from './action-enhancers';


const store = createStore(reducer, undefined, applyMiddleware(createActionEnhancerMiddleware(enhancers)));

Next, create an enhancer

//action-enhancers.js
export const ENHANCE_WITH_TIME = Symbol('ENHANCE WITH TIME');
const timeEnhancer = {
  id: ENHANCE_WITH_TIME,
  mapState: () => {
    return {
      now: new Date()
    };
  }
};

export default [timeEnhancer];

Now use your enhancer to enhance an action

//actions.js
import {ENHANCE_WITH_TIME} from './action-enhancers'

export function enhancedAction(){
  return {
    type: 'ENHANCED_ACTION',
    [ENHANCE_WITH_TIME]: true //action.now will be the time the action was dispatched
  }
}

It's important to note that this action creator is still a pure function (as the middleware is the one actually executing the logic). Calling enhancedAction() directly will not return an object with now on it.

What if you want to enhance your action with a value from your redux state?

//action-enhancers.js
import selectCurrentUser from './selectors';

export const ENHANCE_WITH_CURRENT_USER = Symbol('ENHANCE WITH CURRENT USER');
const userEnhancer = {
  id: ENHANCE_WITH_CURRENT_USER,
  mapState: state => {
    return {
      currentUser: selectCurrentUser(state)
    };
  }
};

const enhancers = [userEnhancer];
export default enhancers;

//actions.js
import {ENHANCE_WITH_CURRENT_USER} from './action-enhancers'

export function enhancedAction(){
  return {
    type: 'ENHANCED_ACTION',
    [ENHANCE_WITH_CURRENT_USER]: true //action.currentUser will be set before being passed to the reducer
  }
}

You can also pass parameters to action enhancers from the action that is being enhanced.

//action-enhancers.js
import getToggleValue from './selectors';

export const ENHANCE_WITH_TOGGLE = Symbol('ENHANCE WITH TOGGLE');
const toggleEnhancer = {
  id: ENHANCE_WITH_TOGGLE,
  mapState: (state, toggle) => {
    return {
      toggleValue: getToggleValue(state, toggle)
    };
  }
};

//actions.js
import {ENHANCE_WITH_TOGGLE} from './action-enhancers'

export function enhancedAction(){
  return {
    type: 'ENHANCED_ACTION',
    [ENHANCE_WITH_TOGGLE]: 'FOO' //action.toggleValue will be the value of the FOO toggle
  }
}

You can also match all actions of certain types rather than actions with a given property

//action-enhancers.js
const fooEnhancer = {
  actionTypes: ['FOO_LOADED', 'BAR_LOADED'],
  mapState: state => {
    return {
      items: getItems(state)
    };
  }
};

export default [fooEnhancer];

Changing action enhnacers after store creation

If you want your list of action enhancers to be dynamic, you can pass a function to createActionEnhancerMiddleware instead of an array.

let actionEnhancers = [...originalEnhancers];
const middleware = [createActionEnhancerMiddleware(() => actionEnhancers)];
const store = createStore(reducer, undefined, applyMiddleware(middleware));
store.addActionEnhancers = newEnhancers => actionEnhancers.push(...newEnhancers);

//in the dynamic loading code
store.addActionEnhancers(newModule.enhancers);

This may be useful if you are dynamically loading portions of your app that define their own action enhancers.

The function is also passed the redux state, so you can determine the available action enhancers based on the state of your application.

TypeScript Support

Follow this pattern to use action enhancers in your TypeScript application.

  1. Define "Enhancement" shape

The type Enhancement is an object, so make a type that is an object.

type CurrentTimeEnhancement = {
  now: Date;
};

Alternatively, make an interface that extends Enhancement.

interface CurrentUserEnhancement extends Enhancement {
  email: string;
}
  1. Define action enhancer
// state for demo purposes
type TestState = {
  lastUpdated: Date | null;
  user: {
    email: string;
  };
  data: string | null;
};

// enhancer with id

const ENHANCE_WITH_CURRENT_TIME = Symbol('ENHANCE_WITH_CURRENT_TIME');

const currentTimeEnhancer: ActionEnhancerById<TestState, CurrentTimeEnhancement> = {
  id: ENHANCE_WITH_CURRENT_TIME,
  mapState: () => ({
    now: new Date()
  })
};

// enhance action types

const currentUserEnhancer: ActionEnhancerByType<TestState, CurrentUserEnhancement> = {
  actionTypes: ['loginSuccess', 'refreshSuccess'],
  mapState: state => ({
    email: state.user.email
  })
};

// enhancer with value

const ENHANCE_WITH_CUSTOM_VALUE = Symbol('ENHANCE_WITH_CUSTOM_VALUE');

type CustomValueEnhancement = {
  value: string;
};

const customValueEnhancer: ActionEnhancerById<TestState, CustomValueEnhancement, string> = {
  id: ENHANCE_WITH_CUSTOM_VALUE,
  mapState: (state, value) => ({
    value
  })
};
  1. Define the "unenhanced" ("base", "internal", "core", whatever you'd like to call it) version of your action, which includes the fields defined by your specific action and then enhancer IDs
export enum actionTypes {
  COOL_STUFF = 'doCoolStuff',
  OTHER_STUFF = 'doOtherStuff'
}

type UnenhancedMyCoolAction = UnenhancedAction<actionTypes.COOL_STUFF> & {
  cool: string;
};
  1. Create an action creator which returns the "core" action with enhancers added
const coolStuff = (value: string): UnenhancedMyCoolAction => ({
  type: actionTypes.COOL_STUFF,
  cool: value,
  [ENHANCE_WITH_CURRENT_TIME]: true
});
  1. Define the enhanced version of the action
type MyCoolAction = EnhancedAction<
  Action<actionTypes.COOL_STUFF>,
  CurrentTimeEnhancement,
  CurrentUserEnhancement,
  CustomValueEnhancement
>;

Generally speaking, the unenhanced action should not need to be used by any code other than the action creator and unit tests; there are notable exceptions to this, such as if you need to reference the unenhanced action type in a function return. Any code that consumes the action from Redux, such as the reducer, should use the enhanced version of the action, which will have the fully realized fields available.

e.g.,

type ReducerActions = MyCoolAction | OtherCoolAction;

function reducer(state: TestState, action: ReducerActions): TestState {
  switch (action.type) {
    case actionTypes.COOL_STUFF:
      return Object.assign({}, state, {
        lastUpdated: action.email ? action.now : null,
        data: action.value
      });
    default:
      return state;
  }
}