{"id":14384766,"url":"https://github.com/adrienjt/redux-data-structures","last_synced_at":"2025-08-23T17:33:36.305Z","repository":{"id":57120092,"uuid":"94646125","full_name":"adrienjt/redux-data-structures","owner":"adrienjt","description":"Reducer factory functions for common data structures: counters, maps, lists (queues, stacks), sets, etc.","archived":false,"fork":false,"pushed_at":"2018-02-17T21:19:42.000Z","size":45,"stargazers_count":157,"open_issues_count":5,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-12-06T02:37:08.962Z","etag":null,"topics":["common-reducers","counter","data-structures","dictionary","higher-order-reducers","map","queue","reducer-composition","reducer-creation","reducer-generator","reducer-makers","redux","set","stack","toggle"],"latest_commit_sha":null,"homepage":"https://redux-data-structures.js.org","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/adrienjt.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}},"created_at":"2017-06-17T19:58:14.000Z","updated_at":"2024-09-05T21:17:23.000Z","dependencies_parsed_at":"2022-08-23T06:50:12.981Z","dependency_job_id":null,"html_url":"https://github.com/adrienjt/redux-data-structures","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrienjt%2Fredux-data-structures","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrienjt%2Fredux-data-structures/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrienjt%2Fredux-data-structures/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrienjt%2Fredux-data-structures/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adrienjt","download_url":"https://codeload.github.com/adrienjt/redux-data-structures/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230716665,"owners_count":18269820,"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","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":["common-reducers","counter","data-structures","dictionary","higher-order-reducers","map","queue","reducer-composition","reducer-creation","reducer-generator","reducer-makers","redux","set","stack","toggle"],"created_at":"2024-08-28T18:01:39.117Z","updated_at":"2024-12-21T12:30:56.940Z","avatar_url":"https://github.com/adrienjt.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# Redux Data Structures\n\n## Introduction\n\nRedux Data Structures is a library of _reducer makers_.\n\nReducer makers help create common reducers like counters, maps, lists (queues, stacks), sets, etc. Most application states can be built by combining a handful of these standardized building blocks.\n\nRedux Data Structures was developed for Redux, but does not depend on it. It can actually be used with any reactive state container, even a custom one; Redux Data Structures doesn't have any dependency.\n\n## Getting Started\n\n```\nnpm install --save redux-data-structures\n```\n\nHere's an example from the [Redux README](https://github.com/reactjs/redux), rewritten with Redux Data Structures:\n\n```javascript\nimport { createStore } from 'redux';\nimport { counter } from 'redux-data-structures';\n\nconst myCounter = counter({\n  incrementActionTypes: ['INCREMENT'],\n  decrementActionTypes: ['DECREMENT'],\n});\n\nconst store = createStore(myCounter);\n\nstore.subscribe(() =\u003e { console.log(store.getState()); });\n\nstore.dispatch({ type: 'INCREMENT' });\n// 1\nstore.dispatch({ type: 'INCREMENT' });\n// 2\nstore.dispatch({ type: 'DECREMENT' });\n// 1\n```\n\n## Configuring Data Structures\n\nHere's a more advanced example--with the same reducer maker--of a counter from 10 to 0, decreasing as a function of the action payload, then reset, representing life points for example:\n\n```javascript\nimport { createStore } from 'redux';\nimport { counter } from 'redux-data-structures';\n\nconst lifePoints = counter({\n  initialState: 10,\n  decrementActionTypes: ['PUNCH', 'KICK'],\n  decrement: action =\u003e action.value,\n  min: () =\u003e 0, // action =\u003e number\n  resetActionTypes: ['INSERT_COIN'],\n});\n\nconst store = createStore(lifePoints);\n\nstore.subscribe(() =\u003e { console.log(store.getState()); });\n\nstore.dispatch({ type: 'PUNCH', value: 5 });\n// 5\nstore.dispatch({ type: 'KICK', value: 7 });\n// 0\nstore.dispatch({ type: 'INSERT_COIN' });\n// 10\n```\n\n## Combining Data Structures\n\nLet's build a classic todo app with Redux Data Structures:\n\n```javascript\nimport { createStore, combineReducers } from 'redux';\nimport { map, set, value } from 'redux-data-structures';\n\nconst todos = map({\n  addActionTypes: ['ADD_TODO'],\n  removeActionTypes: ['REMOVE_TODO'],\n});\n\nconst completedTodos = set({\n  toggleActionTypes: ['TOGGLE_TODO'],\n  removeActionTypes: ['REMOVE_TODO'],\n  keyGetter: action =\u003e action.payload.id,\n});\n\nconst visibilityFilter = value({\n  initialState: 'SHOW_ALL',\n  setActionTypes: ['SET_VISIBILITY_FILTER'],\n  valueGetter: action =\u003e action.payload.filter,\n});\n\nconst rootReducer = combineReducers({\n  todos,\n  completedTodos,\n  visibilityFilter,\n});\n\nconst store = createStore(rootReducer);\n```\n\nThat's all for the store! We've relied heavily on the reducer makers' default options, which presume that:\n1. actions adhere to the [Flux Standard Action](https://github.com/acdlite/flux-standard-action) (actions are plain Javascript object with a `type` and `payload` properties),\n1. and Todos are identified by an `id` property, used as a key in the `todos` map (and the `completetedTodos` set).\n\nNow let's subscribe to the store and dispatch a few actions:\n\n```javascript\nstore.subscribe(() =\u003e { console.log(JSON.stringify(store.getState(), null, 2)); });\n\nstore.dispatch({\n  type: 'ADD_TODO',\n  payload: {\n    id: 0,\n    text: 'Go fishing',\n  },\n});\n// {\n//   \"todos\": {\n//     \"byId\": {\n//       \"0\": {\n//         \"id\": 0,\n//         \"text\": \"Go fishing\"\n//       }\n//     },\n//     \"allIds\": [\n//       0\n//     ]\n//   },\n//   \"completedTodos\": {},\n//   \"visibilityFilter\": \"SHOW_ALL\"\n// }\n```\n\nNotice that `todos` is [normalized, for the reasons explained in the Redux documentation](http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html).\n\n```javascript\nstore.dispatch({\n  type: 'TOGGLE_TODO',\n  payload: { id: 0 },\n});\n// {\n//   \"todos\": {\n//     \"byId\": {\n//       \"0\": {\n//         \"id\": 0,\n//         \"text\": \"Go fishing\"\n//       }\n//     },\n//     \"allIds\": [\n//       0\n//     ]\n//   },\n//   \"completedTodos\": {\n//     \"0\": true\n//   },\n//   \"visibilityFilter\": \"SHOW_ALL\"\n// }\n```\n\nCompared to the original Redux Todo example, we've separated the Todo items (id, text) from their completion state. If needed, they could be combined with a [selector](http://redux.js.org/docs/recipes/ComputingDerivedData.html).\n\n```javascript\nstore.dispatch({\n  type: 'SET_VISIBILITY_FILTER',\n  payload: { filter: 'SHOW_COMPLETED' },\n});\n// {\n//   \"todos\": {\n//     \"byId\": {\n//       \"0\": {\n//         \"id\": 0,\n//         \"text\": \"Go fishing\"\n//       }\n//     },\n//     \"allIds\": [\n//       0\n//     ]\n//   },\n//   \"completedTodos\": {\n//     \"0\": true\n//   },\n//   \"visibilityFilter\": \"SHOW_COMPLETED\"\n// }\nstore.dispatch({\n  type: 'REMOVE_TODO',\n  payload: { id: 0 },\n});\n// {\n//   \"todos\": {\n//     \"byId\": {},\n//     \"allIds\": []\n//   },\n//   \"completedTodos\": {},\n//   \"visibilityFilter\": \"SHOW_COMPLETED\"\n// }\n```\n\nThe `REMOVE_TODO` action is reduced both by the `todos` map and the `completedTodos` set.\n\n## Data Structures\n\nSo far, the following data structures have been implemented (corresponding action types are indicated in parentheses):\n\n- Boolean (set to true, set to false, toggle)\n- Counter (increment, decrement)\n- List (queue or stack: enqueue, dequeue, push, pop)\n- Map (add, remove, change)\n- Set (add, remove, toggle)\n- Value (set)\n\nAll data structures can be reset to their initial state, and, if applicable (for lists, maps, and sets), emptied.\n\n## API\n\nEach reducer maker is a higher-order function of a single `options` object and returns a reducer:\n```javascript\n{ ...options } =\u003e (state, action) =\u003e state\n```\n\nFor each reducer maker, we describe below how the `options` object is destructured, its default property values, and how some specific properties are used.\n\nDefaults can--and in a lot of cases __should__--be overridden.\n\nEach category of actions, e.g., `decrementActionTypes`, is an array of action types (i.e., strings), so that several action types can have the same result (cf. [Configuring Data Structures, above](#configuring-data-structures), where both `PUNCH` and `KICK` decrement `lifePoints`).\n\n### Boolean\n\n```javascript\n{\n  initialState = false,\n  trueActionTypes = [],\n  additionalConditionToTrue = () =\u003e true,\n  falseActionTypes = [],\n  additionalConditionToFalse = () =\u003e true,\n  toggleActionTypes = [],\n  resetActionTypes = [],\n}\n```\n\n`additionalConditionToTrue` and `additionalConditionToFalse` are functions of `action` and are used as such:\n\n```javascript\n// ...\nif (trueActionTypes.includes(action.type) \u0026\u0026 additionalConditionToTrue(action)) {\n  return true;\n} else if (falseActionTypes.includes(action.type) \u0026\u0026 additionalConditionToFalse(action)) {\n  return false;\n}\n// ...\n```\n\nThe default `() =\u003e true` is equivalent to no additional condition.\n\n### Counter\n\n```javascript\n{\n  initialState = 0,\n  incrementActionTypes = [],\n  increment = () =\u003e 1,\n  max,\n  decrementActionTypes = [],\n  decrement = () =\u003e 1,\n  min,\n  resetActionTypes = [],\n}\n```\n\n`increment`, `decrement`, `max`, and `min` are functions of `action`. If `max` is `undefined`, it is not enforced. Same for `min`.\n\n### List\n\n```javascript\n{\n  initialState = [],\n  enqueueActionTypes = [],\n  dequeueActionTypes = [],\n  pushActionTypes = [],\n  popActionTypes = [],\n  itemGetter = action =\u003e action.payload,\n  resetActionTypes = [],\n  emptyActionTypes = [],\n}\n```\n\nA list can be used as a queue or stack. `enqueueActionTypes` and `pushActionTypes` add items to the list, using the `itemGetter`. The default `itemGetter` adds the [Flux Standard Action](https://github.com/acdlite/flux-standard-action) `payload` to the list.\n\n### Map\n\n```javascript\n{\n  initialState = {\n    byId: {},\n    allIds: [],\n  },\n  addActionTypes = [],\n  changeActionTypes = [],\n  removeActionTypes = [],\n  keyGetter = action =\u003e action.payload.id,\n  itemGetter = action =\u003e ({...action.payload}),\n  itemModifier = (item, action) =\u003e ({...item, ...action.payload}),\n  resetActionTypes = [],\n  emptyActionTypes = [],\n}\n```\n\n`map` uses the [normalized state shape recommended by Redux](http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html), as can be seen from the default `initialState`. Warning: if you overwrite `initialState`, use the same format!\n\nThe default `keyGetter` assumes that the action payload has an `id` property. The default `itemModifier` overwrites the item's properties (but does not delete the ones that have disappeared in the new action payload).\n\n### Set\n\n```javascript\n{\n  initialState = {},\n  addActionTypes = [],\n  removeActionTypes = [],\n  toggleActionTypes = [],\n  keyGetter = action =\u003e action.payload,\n  resetActionTypes = [],\n  emptyActionTypes = [],\n}\n```\n\nIn Redux Data Structures, a set's state is a plain Javascript object with boolean properties, i.e. if and only if `key` is in the set, `key` is a property of `state` whose value is `true`. Example:\n```javascript\n{ key: true }\n```\nWhen a key is removed from the set, the corresponding property is deleted from the state object:\n```javascript\n{}\n```\n\n### Value\n\n```javascript\n{\n  initialState = null,\n  setActionTypes = [],\n  valueGetter = action =\u003e action.payload,\n  resetActionTypes = [],\n}\n```\n\n`value` is the simplest data structure (to the extent that calling it a data structure is arguable).\n\n## Performance\n\nRedux Data Structures doesn't focus on performance, but on developer productivity. In most cases, performance won't be an issue. If it is, please write an issue or submit a pull request.\n\n## Contributing\n\nThe code is written in modern Javascript, transpiled with Babel, using Jest for tests. Pull requests are welcome.\n\n## License\n\n[MIT](LICENSE.md)\n\n## Author\n\nAdrien Trouillaud, [Codology.net](https://codology.net)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrienjt%2Fredux-data-structures","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadrienjt%2Fredux-data-structures","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrienjt%2Fredux-data-structures/lists"}