-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
100 lines (81 loc) · 3 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
export type onChangeCallback<actionType> = ((lastAction: actionType) => void);
/**
* gets called when a dispatch with an action is triggered
*/
export interface reducer<stateType, actionType> {
(previousState: stateType, action: actionType): stateType;
}
/**
* thats how a complete store is organized
*/
export interface redchain {
<stateType, actionType>(initValue: stateType, reducer: reducer<stateType, actionType>): store<stateType, actionType>;
}
export interface store<stateType, actionType> {
/**
* this value gets replaced, each time the reducer gets called
*/
state: stateType;
/**
* when the state property should change, thats the way to call it
*/
dispatch(action: actionType): boolean;
/**
* eventlisteners when a dispatch caused a change in state
*/
addOnChange(onChange: onChangeCallback<actionType>): store<stateType, actionType>;
/**
* when a eventlistener is not needed, this function should get called
*/
removeOnChange(removeOnChange: onChangeCallback<actionType>): boolean;
}
const store: redchain = <stateType, actionType>(initValue: stateType, reducer: reducer<stateType, actionType>): store<stateType, actionType> => {
let onChanges: onChangeCallback<actionType>[] = [];
const result: store<stateType, actionType> = {
/**
* holds the actual value of the current store
*/
state: initValue,
/**
* takes listeners, when the reducer returnvalue is triggered they
*/
addOnChange(onChange: onChangeCallback<actionType>): store<stateType, actionType> {
onChanges.push(onChange);
return this;
},
/**
* takes listeners, when the reducer returnvalue is triggered they
* and returns true, when something changed
*/
removeOnChange(removeOnChange: onChangeCallback<actionType>) {
const previousLength = onChanges.length;
onChanges = onChanges.filter((currentOnChange: onChangeCallback<actionType>) => currentOnChange !== removeOnChange);
return previousLength !== onChanges.length;
},
/**
* this function triggers the reducer
* when the returnvalue is unequal to the previous state it will trigger the listeners from addOnChange
*/
dispatch: null as any,
};
/**
* dispatch got added after creating the scope for the result object, to bind the function to this scope
*/
result.dispatch = function (this: store<stateType, actionType>, action: actionType) {
const currentState = reducer(this.state, action);
if (this.state !== currentState) {
this.state = currentState;
onChanges.forEach((onChange) => {
// currently no other listeners will get notified, when the following line will fuck up
// try-catch should be avoided, to improve debuggability
// setTimeout would break the call-stack
// If you have an opinion on this matter, please make a github issue and tell me
onChange(action);
});
return true;
}
return false;
}.bind(result);
return result;
};
export default store;