{"id":13394356,"url":"https://github.com/erikras/ducks-modular-redux","last_synced_at":"2025-05-14T05:10:23.378Z","repository":{"id":41329348,"uuid":"41676006","full_name":"erikras/ducks-modular-redux","owner":"erikras","description":"A proposal for bundling reducers, action types and actions when using Redux","archived":false,"fork":false,"pushed_at":"2022-01-31T03:42:20.000Z","size":494,"stargazers_count":9605,"open_issues_count":36,"forks_count":338,"subscribers_count":121,"default_branch":"master","last_synced_at":"2025-04-11T00:44:36.970Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/erikras.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-31T13:11:57.000Z","updated_at":"2025-04-08T02:14:39.000Z","dependencies_parsed_at":"2022-08-10T01:54:23.828Z","dependency_job_id":null,"html_url":"https://github.com/erikras/ducks-modular-redux","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikras%2Fducks-modular-redux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikras%2Fducks-modular-redux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikras%2Fducks-modular-redux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikras%2Fducks-modular-redux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erikras","download_url":"https://codeload.github.com/erikras/ducks-modular-redux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254076849,"owners_count":22010611,"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":[],"created_at":"2024-07-30T17:01:16.907Z","updated_at":"2025-05-14T05:10:23.352Z","avatar_url":"https://github.com/erikras.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Marks"],"sub_categories":["Articles And Tutorials"],"readme":"\u003cp align=\"center\"\u003e\n\u003ca href=\"https://codefund.io/properties/555/visit-sponsor\"\u003e\n\u003cimg src=\"https://codefund.io/properties/555/sponsor\" /\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n    \n# Ducks: Redux Reducer Bundles\n\n\u003cimg src=\"duck.jpg\" align=\"right\"/\u003e\n\nI find as I am building my redux app, one piece of functionality at a time, I keep needing to add  `{actionTypes, actions, reducer}` tuples for each use case. I have been keeping these in separate files and even separate folders, however 95% of the time, it's only one reducer/actions pair that ever needs their associated actions.\n\nTo me, it makes more sense for these pieces to be bundled together in an isolated module that is self contained, and can even be packaged easily into a library.\n\n## The Proposal\n\n### Example\n\nSee also: [Common JS Example](CommonJs.md).\n\n```javascript\n// widgets.js\n\n// Actions\nconst LOAD   = 'my-app/widgets/LOAD';\nconst CREATE = 'my-app/widgets/CREATE';\nconst UPDATE = 'my-app/widgets/UPDATE';\nconst REMOVE = 'my-app/widgets/REMOVE';\n\n// Reducer\nexport default function reducer(state = {}, action = {}) {\n  switch (action.type) {\n    // do reducer stuff\n    default: return state;\n  }\n}\n\n// Action Creators\nexport function loadWidgets() {\n  return { type: LOAD };\n}\n\nexport function createWidget(widget) {\n  return { type: CREATE, widget };\n}\n\nexport function updateWidget(widget) {\n  return { type: UPDATE, widget };\n}\n\nexport function removeWidget(widget) {\n  return { type: REMOVE, widget };\n}\n\n// side effects, only as applicable\n// e.g. thunks, epics, etc\nexport function getWidget () {\n  return dispatch =\u003e get('/widget').then(widget =\u003e dispatch(updateWidget(widget)))\n}\n\n```\n### Rules\n\nA module...\n\n1. MUST `export default` a function called `reducer()`\n2. MUST `export` its action creators as functions\n3. MUST have action types in the form `npm-module-or-app/reducer/ACTION_TYPE`\n3. MAY export its action types as `UPPER_SNAKE_CASE`, if an external reducer needs to listen for them, or if it is a published reusable library\n\nThese same guidelines are recommended for `{actionType, action, reducer}` bundles that are shared as reusable Redux libraries.\n\n### Name\n\nJava has jars and beans. Ruby has gems. I suggest we call these reducer bundles \"ducks\", as in the last syllable of \"redux\".\n\n### Usage\n\nYou can still do:\n\n```javascript\nimport { combineReducers } from 'redux';\nimport * as reducers from './ducks/index';\n\nconst rootReducer = combineReducers(reducers);\nexport default rootReducer;\n```\n\nYou can still do:\n\n```javascript\nimport * as widgetActions from './ducks/widgets';\n```\n...and it will only import the action creators, ready to be passed to `bindActionCreators()`.\n\n\u003e Actually, it'll also import `default`, which will be the reducer function. It'll add an action creator named `default` that won't work. If that's a problem for you, you should enumerate each action creator when importing.\n\nThere will be some times when you want to `export` something other than an action creator. That's okay, too. The rules don't say that you can *only* `export` action creators. When that happens, you'll just have to enumerate the action creators that you want. Not a big deal.\n\n```javascript\nimport {loadWidgets, createWidget, updateWidget, removeWidget} from './ducks/widgets';\n// ...\nbindActionCreators({loadWidgets, createWidget, updateWidget, removeWidget}, dispatch);\n```\n\n### Example\n\n[React Redux Universal Hot Example](https://github.com/erikras/react-redux-universal-hot-example) uses ducks. See [`/src/redux/modules`](https://github.com/erikras/react-redux-universal-hot-example/tree/master/src/redux/modules).\n\n[Todomvc using ducks.](https://github.com/goopscoop/ga-react-tutorial/tree/6-reduxActionsAndReducers)\n\n### BattleCry generators\n\nThere are configurable [BattleCry](https://github.com/pedsmoreira/battlecry) generators ready to be downloaded and help scaffolding ducks:\n\n```sh\nnpm install -g battlecry\ncry download generator erikras/ducks-modular-redux\ncry init duck\n```\n\nRun `cry --help` to check more info about the generators available;\n\n### Implementation\n\nThe migration to this code structure was [painless](https://github.com/erikras/react-redux-universal-hot-example/commit/3fdf194683abb7c40f3cb7969fd1f8aa6a4f9c57), and I foresee it reducing much future development misery.\n\nAlthough it's completely feasable to implement it without any extra library, there are some tools that might help you:\n\n * [extensible-duck](https://github.com/investtools/extensible-duck) - Implementation of the Ducks proposal. With this library you can create reusable and extensible ducks.\n * [saga-duck](https://github.com/cyrilluce/saga-duck) - Implementation of the Ducks proposal in Typescript with [sagas](https://github.com/redux-saga/redux-saga) in mind. Results in reusable and extensible ducks.\n * [redux-duck](https://github.com/PlatziDev/redux-duck) - Helper function to create Redux modules using the ducks-modular-redux proposal\n * [modular-redux-thunk](https://github.com/benbeadle/modular-redux-thunk) - A ducks-inspired package to help organize actions, reducers, and selectors together - with built-in redux-thunk support for async actions.\n * [molecular-js](https://www.npmjs.com/package/molecular-js) - Set of utilities to ease the development of modular state management patterns with Redux (also known as ducks).\n * [ducks-reducer](https://github.com/drpicox/ducks-reducer) - Function to combine _ducks object_ reducers into one reducer (equivalent to [combineReducers](https://redux.js.org/docs/api/combineReducers.html)), and function [ducks-middleware](https://github.com/drpicox/ducks-middleware) to combine _ducks object_ middleware into one single middleware compatible with [applyMiddleware](https://redux.js.org/docs/api/applyMiddleware.html).\n * [simple-duck](https://github.com/xander27/simple-duck) - Class based implementation of modules system, inspired by ducks-modular-redux. All OOP benefits like inheritance and composition. Support combining of duck-module classes and regular reducer functions using `combineModules` function.\n\nPlease submit any feedback via an issue or a tweet to [@erikras](https://twitter.com/erikras). It will be much appreciated.\n\nHappy coding!\n\n-- Erik Rasmussen\n\n\n### Translation\n\n[한국어](https://github.com/JisuPark/ducks-modular-redux)\n[中文](https://github.com/deadivan/ducks-modular-redux)\n[Türkçe](https://github.com/mfyz/ducks-modular-redux-tr)\n\n---\n\n![C'mon! Let's migrate all our reducers!](migrate.jpg)\n\u003e Photo credit to [Airwolfhound](https://www.flickr.com/photos/24874528@N04/3453886876/).\n\n---\n\n[![Beerpay](https://beerpay.io/erikras/ducks-modular-redux/badge.svg?style=beer-square)](https://beerpay.io/erikras/ducks-modular-redux)  [![Beerpay](https://beerpay.io/erikras/ducks-modular-redux/make-wish.svg?style=flat-square)](https://beerpay.io/erikras/ducks-modular-redux?focus=wish)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikras%2Fducks-modular-redux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferikras%2Fducks-modular-redux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikras%2Fducks-modular-redux/lists"}