{"id":23311929,"url":"https://github.com/k2p-ed/easy-ducks","last_synced_at":"2025-06-23T16:02:39.528Z","repository":{"id":57218404,"uuid":"125077473","full_name":"k2p-ed/easy-ducks","owner":"k2p-ed","description":"A utility to simplify the Redux modular duck pattern","archived":false,"fork":false,"pushed_at":"2018-10-07T19:02:41.000Z","size":298,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-12-08T13:38:43.143Z","etag":null,"topics":["actions","async","ducks","ducks-pattern","react-redux","reducer-creation","reducers","redux","redux-thunk"],"latest_commit_sha":null,"homepage":"","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/k2p-ed.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2018-03-13T15:57:50.000Z","updated_at":"2019-11-03T17:01:33.000Z","dependencies_parsed_at":"2022-08-28T21:00:58.185Z","dependency_job_id":null,"html_url":"https://github.com/k2p-ed/easy-ducks","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k2p-ed%2Feasy-ducks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k2p-ed%2Feasy-ducks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k2p-ed%2Feasy-ducks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/k2p-ed%2Feasy-ducks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/k2p-ed","download_url":"https://codeload.github.com/k2p-ed/easy-ducks/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230596266,"owners_count":18250855,"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":["actions","async","ducks","ducks-pattern","react-redux","reducer-creation","reducers","redux","redux-thunk"],"created_at":"2024-12-20T14:17:21.105Z","updated_at":"2024-12-20T14:17:21.589Z","avatar_url":"https://github.com/k2p-ed.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Easy Ducks\n\n[![travis build](https://img.shields.io/travis/k2p-ed/easy-ducks.svg?style=flat-square)](https://travis-ci.org/k2p-ed/easy-ducks)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)\n\nEasy Ducks is a utility that simplifies the implementation of the [ducks pattern](https://github.com/erikras/ducks-modular-redux) for async actions in Redux. It eliminates the need to manually create reducers (no more switch statements!) or action types, and greatly simplifies action creators.\n\nEasy Ducks automatically handles loading states by adding `loading` and `didLoad` keys to each reducer. When a request is in progress, `loading` will be true, and when a request has previously completed at some point, `didLoad` is set to true. This can be useful if you want to show a different loading state for when there is some pre-existing data, versus the initial load with no data.\n\nBy default, Easy Ducks adds async responses to the reducer by with object spread notation, e.g.,\n\n```js\n(state, action) =\u003e ({ ...state, ...action.response })\n```\n\nYou can override this by providing `resolver` functions to action creators in order to define custom behavior.\n\nEasy Ducks assumes you're using [redux-thunk](https://github.com/gaearon/redux-thunk) middleware in your project.\n\nThe default configuration relies on the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API), so you may need to provide a polyfill for extended browser support. Alternatively, you can use plugins to support other http implementations.\n\n## Installation\n\n```sh\nyarn add easy-ducks\n\n# or\n\nnpm install --save easy-ducks\n```\n\n## Quick Start\n\nFirst create your duck and export the reducer:\n\n```js\n// ducks/users.js\n\nimport { Duck } from 'easy-ducks'\n\nconst duck = new Duck('myDuck', {\n  baseUrl: 'https://myapi.com/api'\n})\n\n// Pass this to redux's combineReducers function\nexport default duck.reducer\n\n// Action creators\nexport const getUser = id =\u003e duck.get(`/users/${id}`)\n\nexport const createUser = (params = {}) =\u003e duck.post('/users', { params })\n\nexport const editUser = (id, params = {}) =\u003e duck.put(`/users/${id}`, { params })\n\nexport const deleteUser = id =\u003e duck.delete(`/users/:id`)\n```\n\nThen add the exported `duck.reducer` to your root reducer:\n\n```js\n// ducks/index.js\n\nimport { combineReducers } from 'redux'\n\nimport users from './users'\n\nexport default combineReducers({ users })\n```\n\n## Options\n\n### Instance Options\n\nThe first constructor argument is a string indicating the name of the duck. The name is used to assemble the Redux action type, so it **must** be unique.\n\nThe format for the generated action types looks like this:\n\n`[{name}] {method}: {status}`\n\nFor example, if you create a duck with the name `myDuck`, the actions for `duck.get()` would look like this:\n\n```js\n// GET begin\n{ type: '[myDuck] GET: BEGIN' }\n\n// GET error\n{ type: '[myDuck] GET: ERROR', error }\n\n// GET success\n{ type: '[myDuck] GET: SUCCESS', response }\n```\n\nThe second constructor argument is an instance configuration object:\n\n| Name         | Type     | Required | Default | Description |\n|--------------|----------|----------|---------|---------|\n| baseUrl      | string   | true     |         | The base url for your api |\n| initialState | object   | false    |         | The default value to be passed to the reducer |\n| plugin       | function | false    |         | Allows for http implementations other than `fetch` |\n| storeParams  | boolean  | false    | false   | Tells Easy Ducks to save any params passed to a request in the reducer |\n\nAs an alternative to `storeParams`, you can include a `params` object in the `initialState`:\n\n```js\nconst duck = new Duck('myDuck', {\n  baseUrl: 'https://myapi.com/api',\n  initialState: {\n    params: {}\n  }\n})\n```\n\n### Request Options\n\nThe first request argument is `path`, which is a string that indicates the remainder of the request URL after the `baseUrl` provided in the constructor.\n\nThe second request argument is an optional configuration object:\n\n| Name         | Type     | Required | Description | Arguments |\n|--------------|----------|----------|-------------|-----------|\n| actionModifiers | object | false | Allows for modifying the dispatched action. | |\n| onError     | function | false | Callback function for request error | `error`, \u0026nbsp;`getState` |\n| onSuccess   | function | false | Callback function for request success | `success`, \u0026nbsp;`getState`\n| params      | object   | false     | Contains any parameters to be passed with the request. | |\n| resolver | function   | false    | Allows custom handling of responses | `state`, \u0026nbsp;`action` | |\n| verb       | string | false    | Specifies an alternate verb to use in the action type. Defaults to the http method, e.g. `get`, `post`, etc. | |\n\n\n### Global configuration\n\nThis package provides a named export called `DuckFactory` to simplify re-use of configuration values across all duck instances.\n\n```js\n// duckFactory.js\n\nimport { DuckFactory } from 'easy-ducks'\n\nimport plugin from 'utils/myPlugin'\n\nconst duckFactory = new DuckFactory({\n  baseUrl: 'https://my-api.com/api',\n  plugin\n})\n\nexport default duckFactory\n```\n\nNow import this instance into your individual duck files and create new ducks from that. Ducks created using this method will inherit any configuration options that you previously specified.\n\n```js\nimport duckFactory from '../duckFactory'\n\nconst duck = duckFactory.create('users')\n\nexport default duck.reducer\n\nexport const getUsers = () =\u003e duck.get('/users')\n```\n\n## Action Modifiers\n\nAction modifiers are functions that allow you to modify the object that is passed to the `dispatch` function. You can provide a modifier for each of the three statuses: `begin`, `success`, and `error`.\n\n| Modifier | Arguments |\n|-----|-----------|\n| begin | `getState` |\n| success | `response`, \u0026nbsp;`getState` |\n| error | `error`, \u0026nbsp;`getState` |\n\nThis functionality can be useful if you're using some type of redux analytics middleware, such as [redux-segment](https://github.com/rangle/redux-segment) to track events based on redux actions.\n\nIn this example, the `analytics` key would be added to the object passed to `dispatch`:\n```js\nconst fetchUser = id =\u003e duck.get(`/users/${id}`, {\n  actionModifiers: {\n    success: (response) =\u003e ({\n      meta: trackEvent('viewed user', { id, name: response.name })\n    })\n  }\n})\n```\n\n### Callbacks\n\nThe dispatch function returns a promise, so if you want to perform actions after the request's success or failure you can do so inside a `.then` block.\n\nFor example, if you wanted to save a response to local storage on success:\n\n```js\nimport localStorage from 'store'\n\nstore.dispatch(fetchUser(1))\n  .then((response) =\u003e {\n    localStorage.set('user', response)\n  })\n```\n\nSometimes you may want to perform some action inside the action creator itself. For this scenario there are two optional callbacks, `onSuccess` and `onError`. These callbacks receive `response` and `error`, respectively, as arguments.\n\nUsing these callbacks, the example above would look like this:\n\n```js\nexport const fetchUser = id =\u003e duck.get(`/users/${id}`, {\n  onSuccess: (response) =\u003e {\n    localStorage.set('user', response)\n  }\n})\n```\n\n### Resolvers\n\nResolvers are functions that allow you to define custom behavior for updating the store. Resolvers take the same arguments as the reducer itself, `state` and `action`, and return the new state on request success.\n\nFor example, if a reducer contains an array of users, and you want to add the new user object returned by your `createUser` action creator, you can define a resolver that adds it to the end of the array.\n\n```js\nexport const createUser = (params = {}) =\u003e duck.post('/something', {\n  params,\n  resolver: (state, action) =\u003e ({ ...state, users: [...state.users, action.response.user] })\n})\n```\n\n### Plugins\n\nEasy Ducks uses `fetch` by default to make http requests, but you can provide plugins if you'd like to use something else. Plugins for `axios` and `fetch` are included with the library.\n\n```js\nimport axiosPlugin from 'easy-ducks/lib/plugins/axios'\n\nconst plugin = axiosPlugin()\n\nconst duck = new Duck('myDuck', {\n  baseUrl: 'https://myapi.com/api',\n  plugin\n})\n```\n\nYou can also provide a configuration object to plugin which will be passed along to the config options for the http solution.\n\n```js\nconst plugin = axiosPlugin({\n  headers: {\n    Authentication: 'my-auth-token'\n  }\n})\n```\n\nSince `fetch` is used by default, you only need to explicitly provide the `fetch` plugin if you want to pass it a custom configuration object.\n\nNotes:\n- If you're using the `axios` plugin, you must have `axios` installed as a package dependency.\n- If you're using `fetch`, query params must be included directly in the `path` string.\n\n#### Writing Plugins\n\nPlugins are simply functions that take an object and map the values to the http library of your choice. The object keys are:\n\n- `baseUrl` - The base URL of your API\n- `method` - e.g., `delete`, `get`, `post`, `put`\n- `path` - The remainder of the request endpoint after `baseUrl`\n- `params` - An object containing request data\n\nTake a look at the plugins in the `src/plugins` directory for some examples.\n\n## Example Project\n\nThis repo includes an example project that you can run to test out the library in action.\n\n```sh\n# Install dependencies\nyarn\n\n# Start the dev server\nyarn start\n```\n\nThen go to `localhost:7000` to view the example project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk2p-ed%2Feasy-ducks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fk2p-ed%2Feasy-ducks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fk2p-ed%2Feasy-ducks/lists"}