- Notifications
You must be signed in to change notification settings - Fork0
Boilerplate free class-based reducer creator. Built with TypeScript. Works with Redux and NGRX. Has integration with immer.
License
fxlrnrpt/reducer-class
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Boilerplate free class-based reducer creator. Built with TypeScript. Works with Redux and NGRX. Has integration withimmer.
Heavily inspired by awesomengrx-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).
Consider using it withflux-action-class.
Run
npm i reducer-classIf you use TypeScript set in you tsconfig.json
"experimentalDecorators":true,"emitDecoratorMetadata":true,
Run
npm i reducer-class reflect-metadataAt the top of your project root file (most probably
index.tsx) addimport'reflect-metadata'
If you use TypeScript set in you tsconfig.json
"experimentalDecorators":true,"emitDecoratorMetadata":true,
import{ActionStandard}from'flux-action-class'import{Action,ActionReflect,ReducerClass}from'reducer-class'classActionCatEatextendsActionStandard<number>{}classActionCatPlayextendsActionStandard<number>{}classActionCatBeAwesomeextendsActionStandard<number>{}interfaceIReducerCatState{energy:number}classReducerCatextendsReducerClass<IReducerCatState>{initialState={energy:100,} @ActionReflectaddEnergy(state:IReducerCatState,action:ActionCatEat){return{energy:state.energy+action.payload,}} @Action(ActionCatPlay,ActionCatBeAwesome)wasteEnegry(state:IReducerCatState,action:ActionCatPlay|ActionCatBeAwesome){return{energy:state.energy-action.payload,}}}constreducer=ReducerCat.create()
import{Action,ActionReflect,ReducerClass}from'reducer-class'classActionCatEat{type='ActionCatEat'constructor(publicpayload:number){}}classActionCatPlay{type='ActionCatPlay'constructor(publicpayload:number){}}classActionCatBeAwesome{type='ActionCatBeAwesome'constructor(publicpayload:number){}}interfaceIReducerCatState{energy:number}classReducerCatextendsReducerClass<IReducerCatState>{initialState={energy:100,} @ActionReflectaddEnergy(state:IReducerCatState,action:ActionCatEat){return{energy:state.energy+action.payload,}} @Action(ActionCatPlay,ActionCatBeAwesome)wasteEnegry(state:IReducerCatState,action:ActionCatPlay|ActionCatBeAwesome){return{energy:state.energy-action.payload,}}}constreducer=ReducerCat.create()
import{Action,ReducerClass}from'reducer-class'constactionTypeCatEat='actionTypeCatEat'constactionTypeCatPlay='actionTypeCatPlay'constactionTypeCatBeAwesome='actionTypeCatBeAwesome'interfaceIReducerCatState{energy:number}classReducerCatextendsReducerClass<IReducerCatState>{initialState={energy:100,} @Action(actionTypeCatEat)addEnergy(state:IReducerCatState,action:{payload:number}){return{energy:state.energy+action.payload,}} @Action(actionTypeCatPlay,actionTypeCatBeAwesome)wasteEnegry(state:IReducerCatState,action:{payload:number}){return{energy:state.energy-action.payload,}}}constreducer=ReducerCat.create()
You might have noticed that
ActionReflectis missing in this version. It's because we no longer use classes for our actions and TypeScript can not provide type metadata.
import{ActionStandard}from'flux-action-class'import{Action,ReducerClass}from'reducer-class'classActionCatEat{}classActionCatPlay{}classActionCatBeAwesome{}classReducerCatextendsReducerClass{initialState={energy:100,} @Action(ActionCatEat)addEnergy(state,action){return{energy:state.energy+action.payload,}} @Action(ActionCatPlay,ActionCatBeAwesome)wasteEnegry(state,action){return{energy:state.energy-action.payload,}}}constreducer=ReducerCat.create()
We can not use
ActionReflectin JavaScript because there's no compiler which provides us with metadata for type reflection.
Be aware, you have to configurebabel to provide you with decorator syntax.
import{Action,ReducerClass}from'reducer-class'constactionTypeCatEat='actionTypeCatEat'constactionTypeCatPlay='actionTypeCatPlay'constactionTypeCatBeAwesome='actionTypeCatBeAwesome'classReducerCat{initialState={energy:100,} @Action(actionTypeCatEat)addEnergy(state,action){return{energy:state.energy+action.payload,}} @Action(actionTypeCatPlay,actionTypeCatBeAwesome)wasteEnegry(state,action){return{energy:state.energy-action.payload,}}}constreducer=ReducerCat.create()
If your reducer expects 3 argumentsreducer-class automatically wraps it withproduce fromimmer.
- Original read-only state
- Draft of the new state that you should mutate
- Action
Why 3?Read pitfall #3 from immer's official documentation.
import{ActionStandard}from'flux-action-class'import{Action,ActionReflect,ReducerClass,Immutable}from'reducer-class'classActionCatEatextendsActionStandard<number>{}classActionCatPlayextendsActionStandard<number>{}classActionCatBeAwesomeextendsActionStandard<number>{}interfaceIReducerCatState{energy:number}classReducerCatextendsReducerClass<IReducerCatState>{initialState={energy:100,} @ActionReflectaddEnergy(state:Immutable<IReducerCatState>,draft:IReducerCatState,action:ActionCatEat){draft.energy+=action.payload} @Action(ActionCatPlay,ActionCatBeAwesome)wasteEnegry(state:Immutable<IReducerCatState>,draft:IReducerCatState,action:ActionCatPlay|ActionCatBeAwesome){draft.energy-=action.payload}}constreducer=ReducerCat.create()
You might have noticed a new import -
Immutable. It's just a cool name forDeepReadonly type. You don't have to use it. The example above would work just fine if used justIReducerCatState. Yet it's recommended to wrap it withImmutableto ensure that you never mutate it.
You can use@ActionReflect 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 propertytype or a propertytype on the instance of the class.
If you have declare several reducer functions corresponding to the same actionreducer-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 byObject.keys.
import{ActionStandard}from'flux-action-class'import{Action,ActionReflect,ReducerClass}from'reducer-class'classActionCatEatextendsActionStandard<number>{}classActionCatSleepextendsActionStandard<number>{}interfaceIReducerCatState{energy:number}classReducerCatextendsReducerClass<IReducerCatState>{initialState={energy:100,} @Action(ActionCatEat,ActionCatSleep)addEnergy(state:IReducerCatState,action:ActionCatEat|ActionCatSleep){return{energy:state.energy+action.payload,}} @ActionReflectaddMoreEnergy(state:IReducerCatState,action:ActionCatSleep){return{energy:state.energy+action.payload*2,}}}constreducer=ReducerCat.create()constres1=reducer(undefined,newActionCatSleep(10))console.log(res1.energy)// logs 130: 100 - initial value, 10 is added by addEnergy, 10 * 2 is added by addMoreEnergyconstres2=reducer(res1,newActionCatEat(5))console.log(res2)// logs 135: 130 - previous value, 5 is added by addEnergy
How does it compare tongrx-actions?
- Stricter typings. Now you'll never forget to add initial state, return a new state from your reducer and accidentally invoke
immeras a result and etc. @ActionReflectcan be used to automatically reflect a corresponding action from the type.ngrx-actionsdoesn't allow matching several reducers to the same action, whilereducer-classallows you to do that and merges them for you.reducer-classis built with both worlds, Angular and Redux, in mind. It means equal support for both of them!
About
Boilerplate free class-based reducer creator. Built with TypeScript. Works with Redux and NGRX. Has integration with immer.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.