{"id":22382357,"url":"https://github.com/jcoreio/redux-utils","last_synced_at":"2025-07-31T03:31:43.939Z","repository":{"id":57297376,"uuid":"54406822","full_name":"jcoreio/redux-utils","owner":"jcoreio","description":"functions for creating and composing reducers and middleware efficiently","archived":false,"fork":false,"pushed_at":"2024-03-06T16:54:43.000Z","size":937,"stargazers_count":1,"open_issues_count":10,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-29T13:50:02.375Z","etag":null,"topics":["middleware","middleware-composition","optimization","performance","reducer-composition","reducer-creation","reducers","redux","scalability"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jcoreio.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-03-21T16:51:24.000Z","updated_at":"2023-11-09T02:29:32.000Z","dependencies_parsed_at":"2024-06-19T10:02:09.976Z","dependency_job_id":"58f70e26-3ac3-4146-86e0-abd67f35abc8","html_url":"https://github.com/jcoreio/redux-utils","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/jcoreio/redux-utils","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-utils","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-utils/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-utils/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-utils/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jcoreio","download_url":"https://codeload.github.com/jcoreio/redux-utils/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcoreio%2Fredux-utils/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267983365,"owners_count":24176058,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-31T02:00:08.723Z","response_time":66,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["middleware","middleware-composition","optimization","performance","reducer-composition","reducer-creation","reducers","redux","scalability"],"created_at":"2024-12-05T00:12:41.200Z","updated_at":"2025-07-31T03:31:43.661Z","avatar_url":"https://github.com/jcoreio.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mindfront-redux-utils\n\n[![CircleCI](https://circleci.com/gh/jcoreio/redux-utils.svg?style=svg)](https://circleci.com/gh/jcoreio/redux-utils)\n[![Coverage Status](https://codecov.io/gh/jcoreio/redux-utils/branch/master/graph/badge.svg)](https://codecov.io/gh/jcoreio/redux-utils)\n[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)\n[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)\n[![npm version](https://badge.fury.io/js/mindfront-redux-utils.svg)](https://badge.fury.io/js/mindfront-redux-utils)\n\n## Making Redux scalable\n\nIf you use [`combineReducers`](http://redux.js.org/docs/api/combineReducers.html) to create your top-level reducer, it will call every one of your subreducers for every action you dispatch. This is easy to debug, and it ensures your state will update correctly, but it's easy to imagine how it will create performance problems.\n\nImagine you're combining 100 subreducers, and you're dispatching actions from a `mousemove` listener at 60 Hz. That's 6000 subreducer calls per second, and it only increases as you add more slices to your state and corresponding subreducers to your app.\n\nThis package and [`mindfront-redux-utils-immutable`](https://github.com/jcoreio/redux-utils-immutable) help you create and combine reducers and middleware in such a way that only the relevant subreducer(s) and middleware for a given action are called, so you don't have to worry that performance will decrease with every subreducer or sub-subreducer (etc) you add.\n\nThere is a downside to this approach: debugging is more difficult, because it's harder to trace where a subreducer is getting called from (or why it's not getting called). This package tries to mitigate that problem as much as it can by saving stack traces of where reducers were created and combined.\n\n## Legacy build note\n\nIf you are building for legacy browsers with webpack or similar bundlers, make sure to add a rule\nto transpile this package with babel.\n\n## createReducer([initialState: any,] actionHandlers: {[actionType: string]: Reducer}): Reducer\n\n```js\nimport { createReducer } from 'mindfront-redux-utils'\n```\n\nCreates a reducer that delegates to `actionHandlers[action.type]`, if it exists, and otherwise, returns `state`.\nIf `initialState` is provided, it will be used as the initial state if the reducer is called with undefined\ninitial state.\n\nThe returned reducer will also have `initialState` and `actionHandlers` as own properties (primarily so that\ncomposeReducers can efficiently compose action map reducers).\n\n## composeReducers(...reducers: Reducer[]): Reducer\n\n```js\nimport { composeReducers } from 'mindfront-redux-utils'\n```\n\nCreates a reducer that applies all the provided reducers.\n\nIf any consecutive reducers have an `actionHandlers` property that is an object (for instance reducers made with\n`createReducer`), `composeReducers` will compose them efficiently: it will group the action handlers by type,\ncompose the handlers for each type separately, and then use `createReducer` on the composed action handlers,\nand initial state from the first reducer for which `initialState` is defined.\n\n## createMiddleware(actionHandlers: {[actionType: string]: Middleware}): Middleware\n\n```js\nimport { createMiddleware } from 'mindfront-redux-utils'\n```\n\nCreates middleware that delegates to `actionHandlers[action.type]`, if it exists, and otherwise,\nreturns `next(action)`.\n\nThe returned middleware will also have `actionHandlers` as an own property.\n\n## composeMiddleware(...middlewares: Middleware[]): Middleware\n\n```js\nimport { composeMiddleware } from 'mindfront-redux-utils'\n```\n\nComposes the given middlewares to be called one after another, just like Redux' `applyMiddleware`, but with one\noptimization: sequences of consecutive middleware that have `actionHandlers` will be recombined into a single middleware\nthat calls any `actionHandlers` for a given action directly.\n\n## applyMiddleware(...middlewares: Middleware[]): createStore =\u003e createStore'\n\n**Requires `redux` as an optional dependency.**\n\n```js\nimport applyMiddleware from 'mindfront-redux-utils/lib/applyMiddleware'\n```\n\nJust like `applyMiddleware` from `redux`, but applies the same optimization as `composeMiddleware`: sequences of\nconsecutive middleware that have `actionHandlers` will be recombined into a single middleware that calls any\n`actionHandlers` for a given action directly.\n\n## combineMiddlewareWithActionHandlers(...middlewares: Middleware[]): Middleware[]\n\n```js\nimport { combineMiddlewareWithActionHandlers } from 'mindfront-redux-utils'\n```\n\nReplaces any sequences of consecutive middleware that have `actionHandlers` in the arguments with a single middleware\nthat calls the `actionHandlers` for a given action directly. (This is used by `composeMiddleware` under the hood).\n\n## createPluggableMiddleware(initialMiddleware: Middleware): Middleware\n\n```js\nimport { createPluggableMiddleware } from 'mindfront-redux-utils'\n```\n\nCreates a middleware that delegates to a hot-swappable middleware. The returned middleware will have a\n`replaceMiddleware(nextMiddleware: Middleware)` function. This way you can use Webpack hot reloading on\nyour custom middleware.\n\n## prefixReducer(prefix: string): (reducer: Reducer) =\u003e Reducer\n\n```js\nimport { prefixReducer } from 'mindfront-redux-utils'\n```\n\nA reducer decorator that strips `prefix` from each `action.type` before passing it to the decorated `reducer`.\nIf the `action.type` doesn't start with `prefix`, it will just return the `state` instead of calling `reducer`.\n\nIf the decorated reducer has `actionHandlers` (from `createReducer`), then the returned reducer will have\n`actionHandlers` with the prefixed action type keys.\n\n### Example\n\n```es6\nimport { combineReducers } from 'redux'\nimport { createReducer, prefixReducer } from 'mindfront-redux-utils'\n\nconst counterReducer = createReducer(0, {\n  DECREMENT: (state) =\u003e state - 1,\n  INCREMENT: (state) =\u003e state + 1,\n})\n\nconst reducer = combineReducers({\n  counter1: prefixReducer('COUNTER_1.')(counterReducer),\n  counter2: prefixReducer('COUNTER_2.')(counterReducer),\n})\n\nreducer({}, { type: 'COUNTER_1.INCREMENT' }) // {counter1: 1}\nreducer({ counter1: 3, counter2: 3 }, { type: 'COUNTER_1.DECREMENT' }) // {counter1: 2, counter2: 3}\nreducer({ counter1: 3, counter2: 3 }, { type: 'COUNTER_2.INCREMENT' }) // {counter1: 3, counter2: 4}\n```\n\n## prefixActionCreator(prefix: string): (actionCreator: ActionCreator) =\u003e ActionCreator\n\n```js\nimport { prefixActionCreator } from 'mindfront-redux-utils'\n```\n\nAn action creator decorator that prepends `prefix` to the `type` of the created actions.\n\n### Example\n\n```es6\nimport { prefixActionCreator } from 'mindfront-redux-utils'\n\nfunction setEntry(key, value) {\n  return {\n    type: 'SET_ENTRY',\n    payload: value,\n    meta: { key },\n  }\n}\n\nconst setConfigEntry = prefixActionCreator('CONFIG.')(setEntry)\n\nsetConfigEntry('hello', 'world').type // CONFIG.SET_ENTRY\n```\n\n## addMeta(meta: Object): (actionCreator: ActionCreator) =\u003e ActionCreator\n\n```js\nimport { addMeta } from 'mindfront-redux-utils'\n```\n\nAn action or action creator decorator that assigns additional properties to actions' `meta`.\n\n### Example\n\n```es6\nimport { addMeta } from 'mindfront-redux-utils'\n\nfunction setEntry(key, value) {\n  return {\n    type: 'SET_ENTRY',\n    payload: value,\n    meta: { key },\n  }\n}\n\nconst forConfigDomain = addMeta({ domain: 'config' })\n\nconst setConfigEntry = forConfigDomain(setEntry)\n\nsetConfigEntry('hello', 'world').meta // {key: 'hello', domain: 'config'}\n\nforConfigDomain(setEntry('hello', 'world')).meta // {key: 'hello', domain: 'config'}\n```\n\n## fullStack(error: Error, wrapped?: (error: Error) =\u003e string): string\n\nErrors thrown from the sub-reducers you pass to `createReducer`, `composeReducers`, 'prefixReducer', or sub-middleware\nyou pass to `createMiddleware` or `composeMiddleware` normally don't include any information about where the associated\ncall to `createReducer` etc. occurred, making debugging difficult. However, in dev mode, `mindfront-redux-utils` adds\nthis info to the resulting reducers and middleware, and you can get it by calling `fullStack`, like so:\n\n```js\nimport { createReducer, fullStack } from './src'\n\nfunction hello() {\n  throw new Error('TEST')\n}\nconst r = createReducer({ hello })\n\ntry {\n  r({}, { type: 'hello' })\n} catch (e) {\n  console.error(fullStack(e))\n}\n```\n\nOutput:\n\n```\nError: TEST\n    at hello (/Users/andy/redux-utils/temp.js:4:9)\n    at result (/Users/andy/redux-utils/src/createReducer.js:19:24)\n    at withCause (/Users/andy/redux-utils/src/addCreationStack.js:5:14)\n    at Object.\u003canonymous\u003e (/Users/andy/redux-utils/temp.js:9:3)\n    at Module._compile (module.js:556:32)\n    at loader (/Users/andy/redux-utils/node_modules/babel-register/lib/node.js:144:5)\n    at Object.require.extensions.(anonymous function) [as .js] (/Users/andy/redux-utils/node_modules/babel-register/lib/node.js:154:7)\n    at Module.load (module.js:473:32)\n    at tryModuleLoad (module.js:432:12)\n    at Function.Module._load (module.js:424:3)\nCaused by reducer created at:\n    at addCreationStack (/Users/andy/redux-utils/src/addCreationStack.js:2:21)\n    at createReducer (/Users/andy/redux-utils/src/createReducer.js:25:55)\n    at Object.\u003canonymous\u003e (/Users/andy/redux-utils/temp.js:6:11)\n    at Module._compile (module.js:556:32)\n    at loader (/Users/andy/redux-utils/node_modules/babel-register/lib/node.js:144:5)\n    at Object.require.extensions.(anonymous function) [as .js] (/Users/andy/redux-utils/node_modules/babel-register/lib/node.js:154:7)\n    at Module.load (module.js:473:32)\n    at tryModuleLoad (module.js:432:12)\n    at Function.Module._load (module.js:424:3)\n    at Function.Module.runMain (module.js:590:10)\n```\n\nIf you are using [VError](https://github.com/joyent/node-verror), you may pass VError's `fullStack` function as the\nsecond argument to also include the cause chain from `VError`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcoreio%2Fredux-utils","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjcoreio%2Fredux-utils","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcoreio%2Fredux-utils/lists"}