Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8b88932

Browse files
author
keenondrums
committed
Make it work with inheritance
1 parent6585e5b commit8b88932

File tree

6 files changed

+175
-7
lines changed

6 files changed

+175
-7
lines changed

‎README.md‎

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Consider using it with [flux-action-class](https://github.com/keenondrums/flux-a
2222
-[Step 1](#step-1)
2323
-[Step 2](#step-2)
2424
-[How can I make shared reducer's logic dynamic?](#how-can-i-make-shared-reducers-logic-dynamic)
25+
-[Reducer inheritance](#reducer-inheritance)
2526
-[In depth](#in-depth)
2627
-[When can we omit list of actions for`@Action`?](#when-can-we-omit-list-of-actions-for-action)
2728
-[Running several reducers for the same action](#running-several-reducers-for-the-same-action)
@@ -669,9 +670,6 @@ const reducer = ReducerCat.create()
669670
```js
670671
import {Action,Extend,ReducerClass }from'reducer-class'
671672

672-
interface IHungryState {
673-
hungry: boolean;
674-
}
675673
exportconstmakeReducerHungry= (actionHungry,actionFull)=>
676674
class {
677675
@Action(actionHungry)
@@ -717,6 +715,76 @@ const reducer = ReducerCat.create()
717715

718716
</details>
719717

718+
##Reducer inheritance
719+
720+
Any 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`_`.
721+
722+
```ts
723+
interfaceICatState {
724+
enegry:number
725+
}
726+
classCatReducerextendsReducerClass<ICatState> {
727+
initialState= {
728+
energy:10,
729+
}
730+
731+
@Action
732+
addEnergy(state:ICatState,action:ActionCatEat) {
733+
returnthis._addEnergy(state,action)
734+
}
735+
736+
// DO NOT FORGET TO PREFIX IT WITH "_"
737+
protected _addEnergy(state:ICatState,action:ActionCatEat):ICatState {
738+
return {
739+
energy:state.energy+action.payload,
740+
}
741+
}
742+
}
743+
744+
classKittenReducerextendsCatReducer {
745+
// DO NOT FORGET TO PREFIX IT WITH "_"
746+
protected _addEnergy(state:ICatState,action:ActionCatEat):ICatState {
747+
return {
748+
energy:state.energy+action.payload*10,
749+
}
750+
}
751+
}
752+
```
753+
754+
<details>
755+
<summary>JavaScript version</summary>
756+
757+
```js
758+
classCatReducerextendsReducerClass {
759+
initialState= {
760+
energy:10,
761+
}
762+
763+
@Action(ActionCatEat)
764+
addEnergy(state,action) {
765+
returnthis._addEnergy(state, action)
766+
}
767+
768+
// DO NOT FORGET TO PREFIX IT WITH "_"
769+
protected_addEnergy(state,action) {
770+
return {
771+
energy:state.energy+action.payload,
772+
}
773+
}
774+
}
775+
776+
classKittenReducerextendsCatReducer {
777+
// DO NOT FORGET TO PREFIX IT WITH "_"
778+
protected_addEnergy(state,action) {
779+
return {
780+
energy:state.energy+action.payload*10,
781+
}
782+
}
783+
}
784+
```
785+
786+
</details>
787+
720788
##In depth
721789

722790
###When can we omit list of actions for`@Action`?

‎package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name":"reducer-class",
3-
"version":"1.3.2",
3+
"version":"1.4.0",
44
"description":"Boilerplate free class-based reducer creator. Built with TypeScript. Works with Redux and NGRX. Has integration with immer.",
55
"main":"dist/index.js",
66
"scripts": {

‎src/constants.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
exportconstMETADATA_KEY_METHOD_PARAMS='design:paramtypes'
22
exportconstMETADATA_KEY_ACTION=Symbol()
3+
exportconstPREFIX_OMIT_METHOD='_'

‎src/reducer-class-helpers.test.ts‎

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,45 @@ describe(ReducerClassHelpers.name, () => {
5252
expect(methodWithActionTypes2.actionType).toBe(Action2.type)
5353
expect(methodWithActionTypes2.method).toBe(mockAddImmerIfNeededResImmer)
5454
})
55+
test('returns action types for the whole inheritance chain',()=>{
56+
classAction1extendsActionStandard{}
57+
classAction2extendsActionStandard{}
58+
classTestextendsReducerClass<undefined>{
59+
publicinitialState=undefined
60+
61+
@Action(Action1)
62+
publicreducerPure():undefined{
63+
returnundefined
64+
}
65+
}
66+
classTestChildextendsTest{
67+
@Action(Action2)
68+
publicreducerImmer(state:undefined,draft:undefined,action:any){
69+
returnundefined
70+
}
71+
}
72+
consttest=newTestChild()
73+
constkeys=reducerClassHelpers.getClassInstanceMethodNames(test)
74+
constspyGetMetadata=jest.spyOn(Reflect,'getMetadata')
75+
constmockAddImmerIfNeededResPure=Symbol()
76+
constmockAddImmerIfNeededResImmer=Symbol()
77+
jest.spyOn(ReducerClassHelpers.prototype,'addImmerIfNeeded').mockImplementation(
78+
(reducer:any):any=>{
79+
if(reducerClassHelpers.typeGuardReducerPure(reducer)){
80+
returnmockAddImmerIfNeededResPure
81+
}
82+
returnmockAddImmerIfNeededResImmer
83+
},
84+
)
85+
constmethodsWithActionTypes=reducerClassHelpers.getReducerClassMethodsWthActionTypes(testasany,keys)
86+
expect(spyGetMetadata).toBeCalledTimes(2)
87+
expect(methodsWithActionTypes.length).toBe(2)
88+
const[methodWithActionTypes1,methodWithActionTypes2]=methodsWithActionTypes
89+
expect(methodWithActionTypes1.actionType).toBe(Action1.type)
90+
expect(methodWithActionTypes1.method).toBe(mockAddImmerIfNeededResPure)
91+
expect(methodWithActionTypes2.actionType).toBe(Action2.type)
92+
expect(methodWithActionTypes2.method).toBe(mockAddImmerIfNeededResImmer)
93+
})
5594
test('throws if no actions passed',()=>{
5695
classTestextendsReducerClass<undefined>{
5796
publicinitialState=undefined
@@ -65,6 +104,25 @@ describe(ReducerClassHelpers.name, () => {
65104
MetadataActionMissingError,
66105
)
67106
})
107+
test("doesn't throw on methods prefixed with '_' if no actions passed",()=>{
108+
classTestextendsReducerClass<undefined>{
109+
publicinitialState=undefined
110+
public_test3(){
111+
returnundefined
112+
}
113+
protected_test2(){
114+
returnundefined
115+
}
116+
117+
//@ts-ignore
118+
private_test(){
119+
returnundefined
120+
}
121+
}
122+
consttest=newTest()
123+
constkeys=reducerClassHelpers.getClassInstanceMethodNames(test)
124+
expect(keys).toEqual([])
125+
})
68126
})
69127

70128
describe(ReducerClassHelpers.prototype.getReducerMap.name,()=>{

‎src/reducer-class-helpers.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import{produce}from'immer'
22
import{DeepReadonlyArray,DeepReadonlyObject}from'typelevel-ts'
33

4-
import{METADATA_KEY_ACTION}from'./constants'
4+
import{METADATA_KEY_ACTION,PREFIX_OMIT_METHOD}from'./constants'
55
import{MetadataActionMissingError}from'./errors'
6+
import{ReducerClass}from'./reducer-class'
67

78
exporttypeImmutable<T>=Textendsobject ?DeepReadonlyObject<T> :Textendsany[] ?DeepReadonlyArray<T> :T
89
exportinterfaceIReducerMap<T>{
@@ -65,8 +66,14 @@ export class ReducerClassHelpers {
6566
returnaccum
6667
},{})
6768
}
68-
publicgetClassInstanceMethodNames(obj:object){
69+
publicgetClassInstanceMethodNames(obj:object):string[]{
6970
constproto=Object.getPrototypeOf(obj)
70-
returnObject.getOwnPropertyNames(proto).filter((name)=>name!=='constructor')
71+
if(!(protoinstanceofReducerClass)){
72+
return[]
73+
}
74+
constprotoMethodNames=Object.getOwnPropertyNames(proto).filter(
75+
(name)=>name!=='constructor'&&!name.startsWith(PREFIX_OMIT_METHOD),
76+
)
77+
return[...this.getClassInstanceMethodNames(proto), ...protoMethodNames]
7178
}
7279
}

‎src/reducer-class.test.ts‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,38 @@ describe(ReducerClass.name, () => {
4747
sum:20,
4848
})
4949
})
50+
test('can be inherited',()=>{
51+
interfaceITestState{
52+
sum:number
53+
}
54+
classAction1extendsActionStandard{}
55+
classTestextendsReducerClass<ITestState>{
56+
publicinitialState={sum:10}
57+
58+
@Action
59+
publictest1(state:ITestState,action:Action1){
60+
returnthis._sum(state)
61+
}
62+
63+
protected_sum(state:ITestState):ITestState{
64+
return{
65+
sum:state.sum+1,
66+
}
67+
}
68+
}
69+
70+
classTestChildextendsTest{
71+
protected_sum(state:ITestState):ITestState{
72+
return{
73+
sum:state.sum+100,
74+
}
75+
}
76+
}
77+
78+
consttestReducer=TestChild.create()
79+
constres11=testReducer(undefined,newAction1())
80+
expect(res11).toEqual({
81+
sum:110,
82+
})
83+
})
5084
})

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp