-
Notifications
You must be signed in to change notification settings - Fork 0
/
simple-state-management.ts
121 lines (109 loc) · 3.71 KB
/
simple-state-management.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
export interface watcher {
id: number;
ref: Function;
}
export class SimpleStateManagement {
private watchCounter = 0;
private watchersPool: Record<string, Array<watcher>>;
private static storeId = "ssm_store";
private static instance: SimpleStateManagement;
constructor() {
if (SimpleStateManagement.instance) {
return SimpleStateManagement.instance;
}
SimpleStateManagement.instance = this;
this.watchersPool = {};
window[SimpleStateManagement.storeId] = {};
}
/**
* Reads and returns current slice data
* @param path - path to state slice
*/
read(path: string) {
const data = this.readFromPath(path, window[SimpleStateManagement.storeId])
return data;
}
/**
* Writes new state slice
* @param path - path to state slice
* @param payload - data to be written
*/
write<T>(path: string, payload: T) {
//Write data
this.writeToPath(path, window[SimpleStateManagement.storeId], payload);
// Notify listeners
this.notifyWatchers(path);
}
/**
* Registers callback function to be called when data slice is modified
* @param path - path to state slice
* @param callback - function to be called
*/
watch(path: string, callback: Function) {
this.watchCounter++;
this.watchersPool[path] = [{id: this.watchCounter, ref: callback}];
const idRef = this.watchCounter;
return () => {
this.unwatch(idRef, path);
}
}
/**
*
* @param idRef Removes watcher from watchersPool
* @param path - path to state slice
*/
private unwatch(idRef: number, path: string) {
const pathWatchers = this.watchersPool[path];
for (const watcher in pathWatchers) {
if (pathWatchers[watcher].id === idRef) {
delete pathWatchers[watcher];
}
}
}
/**
* Calls all watchers update callbacks for given slice
* @param path - path to state slice
*/
private notifyWatchers(path: string) {
if (!this.watchersPool) return;
const pathWatchers = this.watchersPool[path];
for (const watcher in pathWatchers) {
pathWatchers[watcher].ref();
}
}
/**
* Resolves location and gets data
* @param path - path to state slice
* @param obj - reference to store
*/
private readFromPath(path: string, obj: Object) {
const delimiterIndex = path.indexOf("/");
const currPath = path.substring(0, delimiterIndex);
const restPath = path.substring(delimiterIndex + 1, path.length);
if (delimiterIndex === -1) { // Leaf
return obj[path];
} else { // Try go deeper
if (!obj[currPath]) return undefined;
return this.readFromPath(restPath, obj[currPath]);
}
}
/**
* Writes data to proper slice of store
* @param path - path to state slice
* @param obj - reference to store
* @param data - data to write to slice
*/
private writeToPath<T>(path: string, obj: Object, data: T) {
const delimiterIndex = path.indexOf("/");
const currPath = path.substring(0, delimiterIndex);
const restPath = path.substring(delimiterIndex + 1, path.length);
if (delimiterIndex === -1) { // Leaf
obj[path] = data;
} else { // Go deeper and build object if needed
if (!obj[currPath]) {
obj[currPath] = {};
}
this.writeToPath(restPath, obj[currPath], data);
}
}
}