
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
modular-redux-thunk
Advanced tools
A ducks-inspired package to help organize actions, reducers, and selectors together.
A ducks-inspired package to help organize actions, reducers, and selectors together - with built-in redux-thunk support for async actions.
npm install --save modular-redux-thunk
import createStore from 'modular-redux-thunk';
const { store, pickActions, selectors } = createStore(myModules);
You can also include custom reducers, middleware, or enhancers. For example, if you install react-router and redux-freeze:
npm install --save react-router
import createStore from 'modular-redux-thunk';
import { routerReducer } from 'react-router-redux';
const { store, pickActions, selectors } = createStore(myModules, {
reducers: {
routing: routerReducer
},
enhancers: []
});
Let's say your app will be storing the following information in the state:
reducers/chips.js
const actions = {};
const reducers = {};
const selectors = {};
const ACTION_PREPEND = 'my-react-app/chips';
const SET_FAVORITE_CHIPS = `${ACTION_PREPEND}/SET_FAVORITE_CHIPS`;
reducers.favorite = (state = 'unknown', action) => {
switch(action.type) {
case SET_FAVORITE_CHIPS: return action.newFav;
default: return state;
};
};
actions.setFavoriteChips = (newFav) => {
return {
type: SET_FAVORITE_CHIPS,
newFav
};
};
selectors.getFavoriteChips = (chipsState) => chipsState.favorite;
const SET_CHIPS_FOR_SALE = `${ACTION_PREPEND}/SET_CHIPS_FOR_SALE`;
reducers.chipsForSale = (state = [], action) => {
switch(action.type) {
case SET_CHIPS_FOR_SALE: return action.chips;
default: return state;
};
};
actions.setChipsForSale = (chips) => {
return {
type: SET_CHIPS_FOR_SALE,
chips
};
};
selectors.getChipsForSale = (chipsState) => chipsState.chips;
export default { actions, reducers, selectors };
reducers/drinks.js
import { combineModules, settableValue } from 'modular-redux-thunk';
const actions = {};
const reducers = {};
const selectors = {};
const ACTION_PREPEND = 'my-react-app/drinks';
// You can also define individual modules and combine them
const SET_FAVORITE_DRINK = `${ACTION_PREPEND}/SET_FAVORITE_DRINK`;
const favorite = {
reducer: (state = 'unknown', action) => {
switch(action.type) {
case SET_FAVORITE_DRINK: return action.newFav;
default: return state;
};
},
actions: {
setFavoriteDrink: (newFav) => ({
type: SET_FAVORITE_DRINK,
newFav
})
},
selectors: {
getFavoriteDrink: (favoriteDrinkState) => favoriteDrinkState;
}
}
// Or even use a module creator function to automate this common pattern
const drinksForSale = settableValue([], 'getDrinksForSale', 'setDrinksForSale');
export default combineModules({
favorite,
drinksForSale
});
reducers/selectors.js
export const getUserFavorites = (selectors, state) => {
return {
chips: selectors.getFavoriteChips(state),
drink: selectors.getFavoriteDrink(state)
}
};
reducers/actions.js
export const setUserFavorites = (actions, favChips, favDrink) => {
return function(dispatch) {
dispatch(actions.setFavoriteChips(favChips));
dispatch(actions.setFavoriteDrink(favDrink));
};
};
reducers/index.js
import createStore from 'modular-redux-thunk';
import chips from './chips.js';
import drinks from './drinks.js';
import * as globalActions from './actions.js';
import * as globalSelectors from './selectors.js';
const modules = {chips, drinks};
const globals = {
globalActions: globalActions,
globalSelectors: globalSelectors
};
const { store, selectors, pickActions } = createStore(modules, globals);
export { store, selectors, pickActions };
app.js
import React from 'react';
import { Provider, connect } from 'react-redux';
import { store, selectors, pickActions } from './reducers';
// Create the connected component
class _AppComponent extends React.Component {
render() {
const { favorites } = this.props;
const statement = `My favorite kind of chips are ${favorites.chips} and drink is ${favorites.drink}!`;
return (<div>{ statement }</div>);
}
};
_AppComponent.propTypes = {
setFavoriteChips: React.PropTypes.func,
favorites: React.PropTypes.object
};
const AppComponent = connect(state => {
return {
favorites: selectors.getUserFavorites(state)
};
}, pickActions('setFavoriteChips'))(_ChipsComponent);
const AppWrapper = (props) => {
return (<Provider store={ store }><AppComponent /></Provider>);
};
ReactDOM.render(
<AppWrapper />,
document.getElementById('app')
);
A module object consists of:
reducer - A standard Redux reducer function. Each reducer should be defined in it's simplest form. For example, define reducers that are strings, booleans, numbers, or arrays.reducers - An object of Redux reducer functions. If you choose this option, its value will be run through Redux.combineReducers.actions (object): A map of action creator functions. Each action should return an object with, at the very least, a type property.selectors (object): Selector functions that connected components call to get parts of the state. A selector's first and only argument is the module's current state.createStore(modules, [preloadedState], [globalDefinitions], [reduxConfig])Creates a Redux store that combines your reducers into a single and complete state tree.
modules (object): Defines the global structure of the store. Each key represents the modules's location in the store, and the value is the module object itself. Module objects are described above.[preloadedState] (object): Initial state passed to Redux.[globalDefinitions] (object): Pass in any global actions or selectors. Globals are given access to all reducers. You can pass in the following keys:
[globalActions] (object): Actions that can themselves perform actions from any reducer. Global actions differ from reducer actions in that the first argument will always be:
combinedActions (object): All combined actions from reducers. This allows you to reference reducer-defined actions.[globalSelectors] (object): Selectors that have access to all reducer-defined selectors. Global selectors differ from reducer selectors in that the first argument will always be:
combinedSelectors (object): All combined selectors from reducers. This allows you to reference reducer-defined selectors.[reduxConfig] (object): Any custom redux config. You can pass in the following keys:
[reducers] (object): Additional reducers you'd like to be added to the store. For example, if using react-router, you can pass in routing which will be added to the store.[middleware] (array): Any custom middleware to be added to the store. redux-thunk is automatically included as a middleware for your convenience.[enhancers] (array): Any custom enhancers to be added to the store, such as redux-freeze. When not in production, redux-devtools-extension is automatically added for your convenience.Returns an object with the following properties:
store (Store): A Redux store that lets you read the state, dispatch actionspickActions (function): A function that returns an object of desired actions out of all available actions. Use this instead of passing the actions object to your connected components.selectors (object): All combined selectors for use when connecting your components.actions (object): All available actions. You can cherry-pick actions here as opposed to using pickActions.combineModules(modules)Takes a map of Module objects and returns a single Module object.
settableValue(initialValue, selectorName, actionName, [actionType])Creates a module that controls a single value and responds to a single "set" action, as is quite common in Redux.
initialValue - The initial value of the moduleselectorName - The name of the module's single selector - usually something like getMyValueactionName - The name of the module's single action creator - usually something like setMyValueactionType - Optional. The action's type constant - usually something like SET_MY_VALUE. If not set, it will default to actionName.Commit all changes
npm run build # runs "npm test && npm run clean:build && npm run build && npm run test:build"
npm version "v1.0.0-beta1" -m "Message"
npm publish
git push origin HEAD:master --tags
# Update Changelog
FAQs
A ducks-inspired package to help organize actions, reducers, and selectors together.
We found that modular-redux-thunk demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.