{"id":16368682,"url":"https://github.com/aigoncharov/reducer-class","last_synced_at":"2025-03-23T02:33:43.169Z","repository":{"id":57350056,"uuid":"166861092","full_name":"aigoncharov/reducer-class","owner":"aigoncharov","description":"Boilerplate free class-based reducer creator. Built with TypeScript. Works with Redux and NGRX. Has integration with immer.","archived":false,"fork":false,"pushed_at":"2019-03-04T05:24:06.000Z","size":786,"stargazers_count":26,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-18T00:42:01.215Z","etag":null,"topics":["class","decorators","ngrx","reducer","reducer-creation","reducer-generator","redux","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/aigoncharov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-21T18:31:28.000Z","updated_at":"2023-07-13T23:30:21.000Z","dependencies_parsed_at":"2022-09-16T02:12:26.245Z","dependency_job_id":null,"html_url":"https://github.com/aigoncharov/reducer-class","commit_stats":null,"previous_names":["keenondrums/reducer-class"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aigoncharov%2Freducer-class","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aigoncharov%2Freducer-class/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aigoncharov%2Freducer-class/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aigoncharov%2Freducer-class/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aigoncharov","download_url":"https://codeload.github.com/aigoncharov/reducer-class/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245048011,"owners_count":20552431,"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":["class","decorators","ngrx","reducer","reducer-creation","reducer-generator","redux","typescript"],"created_at":"2024-10-11T02:53:31.571Z","updated_at":"2025-03-23T02:33:42.891Z","avatar_url":"https://github.com/aigoncharov.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# reducer-class [![Build Status](https://travis-ci.org/keenondrums/reducer-class.svg?branch=master)](https://travis-ci.org/keenondrums/reducer-class) [![Coverage Status](https://coveralls.io/repos/github/keenondrums/reducer-class/badge.svg)](https://coveralls.io/github/keenondrums/reducer-class) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Boilerplate%20free%20class-based%20reducer%20creator\u0026url=https://github.com/keenondrums/reducer-class\u0026hashtags=javascript,typescript,redux,flux,ngrx,reducer,class)\n\nBoilerplate free class-based reducer creator. Built with TypeScript. Works with Redux and NGRX. Has integration with [immer](https://github.com/mweststrate/immer).\n\nHeavily inspired by awesome [ngrx-actions](https://github.com/amcdnl/ngrx-actions). It's pretty much a re-write of its reducer-related functionality with stricter typings, usage of reflected typed and leaving aside Angular-only functionality. This library is framework-agnostic and should work with any Redux implementation (Redux, NGRX).\n\nConsider using it with [flux-action-class](https://github.com/keenondrums/flux-action-class).\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Installation](#installation)\n  - [Angular](#angular)\n  - [React](#react)\n- [Quick start](#quick-start)\n  - [Recommended (with flux-action-class)](#recommended-with-flux-action-class)\n  - [Classic NGRX actions](#classic-ngrx-actions)\n  - [With redux-actions](#with-redux-actions)\n  - [Old school: action type constants](#old-school-action-type-constants)\n- [Integration with `immer`](#integration-with-immer)\n- [Reusing reducers](#reusing-reducers)\n  - [Step 1](#step-1)\n  - [Step 2](#step-2)\n  - [How can I make shared reducer's logic dynamic?](#how-can-i-make-shared-reducers-logic-dynamic)\n- [Reducer inheritance](#reducer-inheritance)\n- [In depth](#in-depth)\n  - [When can we omit list of actions for `@Action`?](#when-can-we-omit-list-of-actions-for-action)\n  - [Running several reducers for the same action](#running-several-reducers-for-the-same-action)\n  - [How does @Extend work?](#how-does-extend-work)\n- [How does it compare to ngrx-actions?](#how-does-it-compare-to-ngrx-actions)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Installation\n\n### Angular\n\n1. Run\n   ```\n   npm i reducer-class immer\n   ```\n1. If you use TypeScript set in you tsconfig.json\n\n   ```json\n   \"experimentalDecorators\": true,\n   \"emitDecoratorMetadata\": true,\n   ```\n\n1. If you use JavaScript configure your babel to support decorators and class properties\n\n### React\n\n1. Run\n   ```\n   npm i reducer-class immer reflect-metadata\n   ```\n1. At the top of your project root file (most probably `index.tsx`) add\n   ```ts\n   import 'reflect-metadata'\n   ```\n1. If you use TypeScript set in you tsconfig.json\n\n   ```json\n   \"experimentalDecorators\": true,\n   \"emitDecoratorMetadata\": true,\n   ```\n\n1. If you use JavaScript configure your babel to support decorators and class properties\n\n## Quick start\n\n### Recommended (with [flux-action-class](https://github.com/keenondrums/flux-action-class))\n\n```ts\nimport { ActionStandard } from 'flux-action-class'\nimport { Action, ReducerClass } from 'reducer-class'\n\nclass ActionCatEat extends ActionStandard\u003cnumber\u003e {}\nclass ActionCatPlay extends ActionStandard\u003cnumber\u003e {}\nclass ActionCatBeAwesome extends ActionStandard\u003cnumber\u003e {}\n\ninterface IReducerCatState {\n  energy: number\n}\nclass ReducerCat extends ReducerClass\u003cIReducerCatState\u003e {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action\n  addEnergy(state: IReducerCatState, action: ActionCatEat) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(ActionCatPlay, ActionCatBeAwesome)\n  wasteEnegry(state: IReducerCatState, action: ActionCatPlay | ActionCatBeAwesome) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eJavaScript version\u003c/summary\u003e\n\n```js\nimport { ActionStandard } from 'flux-action-class'\nimport { Action, ReducerClass } from 'reducer-class'\n\nclass ActionCatEat extends ActionStandard {}\nclass ActionCatPlay extends ActionStandard {}\nclass ActionCatBeAwesome extends ActionStandard {}\n\nclass ReducerCat extends ReducerClass {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(ActionCatEat)\n  addEnergy(state, action) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(ActionCatPlay, ActionCatBeAwesome)\n  wasteEnegry(state, action) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003e We can not use `Action` without arguments in JavaScript because there's no compiler which provides us with metadata for type reflection.\n\n\u003c/details\u003e\n\n### Classic NGRX actions\n\n```ts\nimport { Action, ReducerClass } from 'reducer-class'\n\nclass ActionCatEat {\n  type = 'ActionCatEat'\n  constructor(public payload: number) {}\n}\nclass ActionCatPlay {\n  type = 'ActionCatPlay'\n  constructor(public payload: number) {}\n}\nclass ActionCatBeAwesome {\n  type = 'ActionCatBeAwesome'\n  constructor(public payload: number) {}\n}\n\ninterface IReducerCatState {\n  energy: number\n}\nclass ReducerCat extends ReducerClass\u003cIReducerCatState\u003e {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action\n  addEnergy(state: IReducerCatState, action: ActionCatEat) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(ActionCatPlay, ActionCatBeAwesome)\n  wasteEnegry(state: IReducerCatState, action: ActionCatPlay | ActionCatBeAwesome) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eJavaScript version\u003c/summary\u003e\n\n```js\nimport { Action, ReducerClass } from 'reducer-class'\n\nclass ActionCatEat {\n  type = 'ActionCatEat'\n  constructor(payload) {\n    this.payload = payload\n  }\n}\nclass ActionCatPlay {\n  type = 'ActionCatPlay'\n  constructor(payload) {\n    this.payload = payload\n  }\n}\nclass ActionCatBeAwesome {\n  type = 'ActionCatBeAwesome'\n  constructor(payload) {\n    this.payload = payload\n  }\n}\n\nclass ReducerCat extends ReducerClass {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(ActionCatEat)\n  addEnergy(state, action) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(ActionCatPlay, ActionCatBeAwesome)\n  wasteEnegry(state, action) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003e We can not use `Action` without arguments in JavaScript because there's no compiler which provides us with metadata for type reflection.\n\n\u003c/details\u003e\n\n### With [redux-actions](https://github.com/redux-utilities/redux-actions)\n\n```ts\nimport { Action, ReducerClass } from 'reducer-class'\nimport { createAction } from 'redux-actions'\n\nconst actionCatEat = createAction('actionTypeCatEat')\nconst actionCatPlay = createAction('actionTypeCatPlay')\nconst actionCatBeAwesome = createAction('actionTypeCatBeAwesome')\n\ninterface IReducerCatState {\n  energy: number\n}\nclass ReducerCat extends ReducerClass\u003cIReducerCatState\u003e {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(actionCatEat)\n  addEnergy(state: IReducerCatState, action: { payload: number }) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(actionCatPlay, actionCatBeAwesome)\n  wasteEnegry(state: IReducerCatState, action: { payload: number }) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003e You might have noticed that we always pass actions to `Action` in this version. It's because we no longer use classes for our actions and TypeScript can not provide type metadata.\n\n\u003cdetails\u003e\n\u003csummary\u003eJavaScript version\u003c/summary\u003e\n\n```js\nimport { Action, ReducerClass } from 'reducer-class'\nimport { createAction } from 'redux-actions'\n\nconst actionCatEat = createAction('actionTypeCatEat')\nconst actionCatPlay = createAction('actionTypeCatPlay')\nconst actionCatBeAwesome = createAction('actionTypeCatBeAwesome')\n\nclass ReducerCat extends ReducerClass {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(actionCatEat)\n  addEnergy(state, action: { payload }) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(actionCatPlay, actionCatBeAwesome)\n  wasteEnegry(state, action: { payload }) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003c/details\u003e\n\n### Old school: action type constants\n\n```ts\nimport { Action, ReducerClass } from 'reducer-class'\n\nconst actionTypeCatEat = 'actionTypeCatEat'\nconst actionTypeCatPlay = 'actionTypeCatPlay'\nconst actionTypeCatBeAwesome = 'actionTypeCatBeAwesome'\n\ninterface IReducerCatState {\n  energy: number\n}\nclass ReducerCat extends ReducerClass\u003cIReducerCatState\u003e {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(actionTypeCatEat)\n  addEnergy(state: IReducerCatState, action: { payload: number }) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(actionTypeCatPlay, actionTypeCatBeAwesome)\n  wasteEnegry(state: IReducerCatState, action: { payload: number }) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003e You might have noticed that we always pass actions to `Action` in this version. It's because we no longer use classes for our actions and TypeScript can not provide type metadata.\n\n\u003cdetails\u003e\n\u003csummary\u003eJavaScript version\u003c/summary\u003e\n\n```js\nimport { Action, ReducerClass } from 'reducer-class'\n\nconst actionTypeCatEat = 'actionTypeCatEat'\nconst actionTypeCatPlay = 'actionTypeCatPlay'\nconst actionTypeCatBeAwesome = 'actionTypeCatBeAwesome'\n\nclass ReducerCat {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(actionTypeCatEat)\n  addEnergy(state, action) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(actionTypeCatPlay, actionTypeCatBeAwesome)\n  wasteEnegry(state, action) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003c/details\u003e\n\n## Integration with `immer`\n\nIf your reducer expects 3 arguments `reducer-class` automatically wraps it with `produce` from [immer](https://github.com/mweststrate/immer).\n\n1. Original read-only state\n1. Draft of the new state that you should mutate\n1. Action\n\nWhy 3? [Read pitfall #3 from immer's official documentation.](https://github.com/mweststrate/immer#pitfalls)\n\n```ts\nimport { ActionStandard } from 'flux-action-class'\nimport { Action, ReducerClass, Immutable } from 'reducer-class'\n\nclass ActionCatEat extends ActionStandard\u003cnumber\u003e {}\nclass ActionCatPlay extends ActionStandard\u003cnumber\u003e {}\nclass ActionCatBeAwesome extends ActionStandard\u003cnumber\u003e {}\n\ninterface IReducerCatState {\n  energy: number\n}\nclass ReducerCat extends ReducerClass\u003cIReducerCatState\u003e {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action\n  addEnergy(state: Immutable\u003cIReducerCatState\u003e, draft: IReducerCatState, action: ActionCatEat) {\n    draft.energy += action.payload\n  }\n\n  @Action(ActionCatPlay, ActionCatBeAwesome)\n  wasteEnegry(state: Immutable\u003cIReducerCatState\u003e, draft: IReducerCatState, action: ActionCatPlay | ActionCatBeAwesome) {\n    draft.energy -= action.payload\n    // Unfortunatelly, we can not omit `return` statement here due to how TypeScript handles `void`\n    // https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void\n    return undefined\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003e As you can see we still return `undefined` from the reducer even though we use [immer](https://github.com/mweststrate/immer) and mutate our draft. Unfortunately, we can not omit `return` statement here due to [how TypeScript handles `void`](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-functions-returning-non-void-assignable-to-function-returning-void). We can not even write `return` (withour `undefined`), because TypeScript then presumes the method returns `void`.\n\n\u003e You might have noticed a new import - `Immutable`. It's just a cool name for [DeepReadonly type](https://github.com/gcanti/typelevel-ts#deepreadonly). You don't have to use it. The example above would work just fine if used just `IReducerCatState`. Yet it's recommended to wrap it with `Immutable` to ensure that you never mutate it.\n\n\u003e Actually it makes total sense to use `Immutable` for state of regular reducers as well to make sure you never modify state directly.\n\n## Reusing reducers\n\nSo what if we want to share some logic between reducers?\n\n### Step 1\n\nCreate a class with shared logic.\n\n```ts\nimport { Action, ReducerClassMixin } from 'reducer-class'\n\ninterface IHungryState {\n  hungry: boolean\n}\nexport class ReducerHungry\u003cT extends IHungryState\u003e extends ReducerClassMixin\u003cT\u003e {\n  @Action(ActionHungry)\n  hugry(state: T) {\n    return {\n      ...state,\n      hungry: true,\n    }\n  }\n\n  @Action(ActionFull)\n  full(state: T) {\n    return {\n      ...state,\n      hungry: false,\n    }\n  }\n}\n```\n\n\u003e You might have noticed that made this class generic. We have to do that because we do not know what actual state we going to extend, we can only put a constraint on it to make sure it satisfies the structure we need. In other words, if we used `IHungryState` directly and returned `{ hungry: true }` (not `{ ...state, hungry: true }`) from `hungry` compiler wouldn't complain.\n\n\u003e You don't have to use `ReducerClassMixin` class. It's nothing but a convenience wrapper to make sure your class carries an index signature for type-safety. Alternatively you can use `IReducerClassConstraint` interface and `ReducerClassMethod` type.\n\n\u003cdetails\u003e\n\u003csummary\u003eHow to use `IReducerClassConstraint` interface and `ReducerClassMethod` type instead of `ReducerClassMixin` class\u003c/summary\u003e\n\n```ts\nimport { Action, IReducerClassConstraint, ReducerClassMethod } from 'reducer-class'\n\ninterface IHungryState {\n  hungry: boolean\n}\nexport class ReducerHungry\u003cT extends IHungryState\u003e implements IReducerClassConstraint\u003cT\u003e {\n  [methodName: string]: ReducerClassMethod\u003cT\u003e\n\n  @Action(ActionHungry)\n  hugry(state: T) {\n    return {\n      ...state,\n      hungry: true,\n    }\n  }\n\n  @Action(ActionFull)\n  full(state: T) {\n    return {\n      ...state,\n      hungry: false,\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eJavaScript version\u003c/summary\u003e\n\n```js\nimport { Action } from 'reducer-class'\n\nexport class ReducerHungry {\n  @Action(ActionHungry)\n  hugry(state) {\n    return {\n      ...state,\n      hungry: true,\n    }\n  }\n\n  @Action(ActionFull)\n  full(state) {\n    return {\n      ...state,\n      hungry: false,\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n### Step 2\n\nUse @Extend decorator.\n\n```ts\nimport { Action, Extend, ReducerClass } from 'reducer-class'\n\nimport { ReducerHungry } from 'shared'\n\ninterface ICatState {\n  hugry: boolean\n  enegry: number\n}\n@Extend\u003cICatState\u003e(ReducerHungry)\nclass CatReducer extends ReducerClass\u003cICatState\u003e {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action\n  addEnergy(state: ICatState, action: ActionCatEat) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(ActionCatPlay, ActionCatBeAwesome)\n  wasteEnegry(state: ICatState, action: ActionCatPlay | ActionCatBeAwesome) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003e @Extend can accept as many arguments as you want.\n\nNow our cat reducer uses `wasteEnegry` to handle actions `ActionCatPlay`, `ActionCatBeAwesome`, `addEnergy` to handle `ActionCatEat` and inherits `hugry` and `full` methods to handle `ActionHungry` and `ActionFull` from `ReducerHungry`.\n\n\u003cdetails\u003e\n\u003csummary\u003eJavaScript version\u003c/summary\u003e\n\n```js\nimport { Action, Extend, ReducerClass } from 'reducer-class'\n\nimport { ReducerHungry } from 'shared'\n\n@Extend(ReducerHungry)\nclass CatReducer extends ReducerClass {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(ActionCatEat)\n  addEnergy(state, action) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(ActionCatPlay, ActionCatBeAwesome)\n  wasteEnegry(state, action) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003c/details\u003e\n\n### How can I make shared reducer's logic dynamic?\n\nYou can use class factories.\n\n```ts\nimport { Action, Extend, ReducerClass, ReducerClassMixin } from 'reducer-class'\n\ninterface IHungryState {\n  hungry: boolean\n}\nexport const makeReducerHungry = \u003cT extends IHungryState\u003e(actionHungry, actionFull) =\u003e {\n  class Extender1 extends ReducerClassMixin\u003cT\u003e {\n    @Action(actionHungry)\n    hugry(state: T) {\n      return {\n        ...state,\n        hungry: true,\n      }\n    }\n\n    @Action(actionFull)\n    full(state: T) {\n      return {\n        ...state,\n        hungry: false,\n      }\n    }\n  }\n  return Extender1\n}\n\ninterface ICatState {\n  hugry: boolean\n  enegry: number\n}\n@Extend\u003cICatState\u003e(makeReducerHungry(ActionCatPlay, ActionCatEat))\nclass CatReducer extends ReducerClass\u003cICatState\u003e {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action\n  addEnergy(state: ICatState, action: ActionCatEat) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action\n  wasteEnegry(state: ICatState, action: ActionCatPlay) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eJavaScript version\u003c/summary\u003e\n\n```js\nimport { Action, Extend, ReducerClass } from 'reducer-class'\n\nexport const makeReducerHungry = (actionHungry, actionFull) =\u003e\n  class {\n    @Action(actionHungry)\n    hugry(state) {\n      return {\n        ...state,\n        hungry: true,\n      }\n    }\n\n    @Action(actionFull)\n    full(state) {\n      return {\n        ...state,\n        hungry: false,\n      }\n    }\n  }\n\n@Extend(makeReducerHungry(ActionCatPlay, ActionCatEat))\nclass CatReducer extends ReducerClass {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(ActionCatEat)\n  addEnergy(state, action) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action(ActionCatPlay)\n  wasteEnegry(state, action) {\n    return {\n      energy: state.energy - action.payload,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n```\n\n\u003c/details\u003e\n\n## Reducer inheritance\n\nAny reducer class is still a class, therefore it can be inherited. It's different way to share some common logic and alter the final behavior for children. There's no runtime information about method visibility (`private`, `protected`, `public`), so if you want to share some common logic without wrapping it with `@Action` decorator prefix the shared method with `_`.\n\n```ts\ninterface ICatState {\n  enegry: number\n}\nclass CatReducer extends ReducerClass\u003cICatState\u003e {\n  initialState = {\n    energy: 10,\n  }\n\n  @Action\n  addEnergy(state: ICatState, action: ActionCatEat) {\n    return this._addEnergy(state, action)\n  }\n\n  // DO NOT FORGET TO PREFIX IT WITH \"_\"\n  protected _addEnergy(state: ICatState, action: ActionCatEat): ICatState {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n}\n\nclass KittenReducer extends CatReducer {\n  // DO NOT FORGET TO PREFIX IT WITH \"_\"\n  protected _addEnergy(state: ICatState, action: ActionCatEat): ICatState {\n    return {\n      energy: state.energy + action.payload * 10,\n    }\n  }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eJavaScript version\u003c/summary\u003e\n\n```js\nclass CatReducer extends ReducerClass {\n  initialState = {\n    energy: 10,\n  }\n\n  @Action(ActionCatEat)\n  addEnergy(state, action) {\n    return this._addEnergy(state, action)\n  }\n\n  // DO NOT FORGET TO PREFIX IT WITH \"_\"\n  protected _addEnergy(state, action) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n}\n\nclass KittenReducer extends CatReducer {\n  // DO NOT FORGET TO PREFIX IT WITH \"_\"\n  protected _addEnergy(state, action) {\n    return {\n      energy: state.energy + action.payload * 10,\n    }\n  }\n}\n```\n\n\u003c/details\u003e\n\n## In depth\n\n### When can we omit list of actions for `@Action`?\n\nYou can omit list of actions for `@Action` if you want to run a reducer function for a single action. **Works with TypeScript only!** Action must be a class-based action. It can be a flux-action-class' action, a classic NGRX class-based action or any other class which has either a static property `type` or a property `type` on the instance of the class.\n\n### Running several reducers for the same action\n\nIf you have declare several reducer functions corresponding to the same action `reducer-class` runs all of them serially. It uses its own implementation of [reduce-reducers](https://github.com/redux-utilities/reduce-reducers). The order is defined by [Object.keys](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys).\n\n```ts\nimport { ActionStandard } from 'flux-action-class'\nimport { Action, ReducerClass } from 'reducer-class'\n\nclass ActionCatEat extends ActionStandard\u003cnumber\u003e {}\nclass ActionCatSleep extends ActionStandard\u003cnumber\u003e {}\n\ninterface IReducerCatState {\n  energy: number\n}\nclass ReducerCat extends ReducerClass\u003cIReducerCatState\u003e {\n  initialState = {\n    energy: 100,\n  }\n\n  @Action(ActionCatEat, ActionCatSleep)\n  addEnergy(state: IReducerCatState, action: ActionCatEat | ActionCatSleep) {\n    return {\n      energy: state.energy + action.payload,\n    }\n  }\n\n  @Action\n  addMoreEnergy(state: IReducerCatState, action: ActionCatSleep) {\n    return {\n      energy: state.energy + action.payload * 2,\n    }\n  }\n}\n\nconst reducer = ReducerCat.create()\n\nconst res1 = reducer(undefined, new ActionCatSleep(10))\nconsole.log(res1.energy) // logs 130: 100 - initial value, 10 is added by addEnergy, 10 * 2 is added by addMoreEnergy\nconst res2 = reducer(res1, new ActionCatEat(5))\nconsole.log(res2) // logs 135: 130 - previous value, 5 is added by addEnergy\n```\n\n### How does @Extend work?\n\nIt iterates over its arguments and copies their methods and corresponding metadata to a prototype of our target reducer class.\n\n## How does it compare to [ngrx-actions](https://github.com/amcdnl/ngrx-actions)?\n\n1. Stricter typings. Now you'll never forget to add initial state, return a new state from your reducer and accidentally invoke `immer` as a result and etc.\n1. `@Action` can be used to automatically reflect a corresponding action from the type.\n1. `ngrx-actions` doesn't allow matching several reducers to the same action, while `reducer-class` allows you to do that and merges them for you.\n1. `reducer-class` is built with both worlds, Angular and Redux, in mind. It means equal support for all of them!\n1. `reducer-class` works with function-based action creators and supports [redux-actions](https://github.com/redux-utilities/redux-actions) out-of-the-box.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faigoncharov%2Freducer-class","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faigoncharov%2Freducer-class","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faigoncharov%2Freducer-class/lists"}