-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
Copy pathStoreObserver.ts
103 lines (83 loc) · 2.71 KB
/
StoreObserver.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
import {any, filter, forEach, map, path} from 'ramda';
import {Store, Unsubscribe} from 'redux';
type Observer<TStore> = (store: TStore) => void;
type UnregisterObserver = () => void;
interface IStoreObserverState<TStore> {
inputPaths: string[][];
lastState: any;
observer: Observer<TStore>;
triggered: boolean;
}
export interface IStoreObserverDefinition<TStore> {
observer: Observer<Store<TStore>>;
inputs: string[];
}
export default class StoreObserver<TStore> {
private _store?: Store<TStore>;
private _unsubscribe?: Unsubscribe;
private readonly _observers: IStoreObserverState<Store<TStore>>[] = [];
constructor(store?: Store<TStore>) {
this.__init__(store);
}
observe = (
observer: IStoreObserverDefinition<TStore> | Observer<Store<TStore>>,
inputs?: string[]
): UnregisterObserver => {
if (typeof observer === 'function') {
if (!Array.isArray(inputs)) {
throw new Error('inputs must be an array');
}
this.add(observer, inputs);
return () => this.remove(observer);
} else {
this.add(observer.observer, observer.inputs);
return () => this.remove(observer.observer);
}
};
setStore = (store: Store<TStore>) => {
this.__finalize__();
this.__init__(store);
};
private __finalize__ = () => this._unsubscribe?.();
private __init__ = (store?: Store<TStore>) => {
this._store = store;
if (store) {
this._unsubscribe = store.subscribe(this.notify);
}
forEach(o => (o.lastState = null), this._observers);
};
private add = (observer: Observer<Store<TStore>>, inputs: string[]) =>
this._observers.push({
inputPaths: map(p => p.split('.'), inputs),
lastState: null,
observer,
triggered: false
});
private notify = () => {
const store = this._store;
if (!store) {
return;
}
const state = store.getState();
const triggered = filter(
o =>
!o.triggered &&
any(i => path(i, state) !== path(i, o.lastState), o.inputPaths),
this._observers
);
forEach(o => (o.triggered = true), triggered);
forEach(o => {
o.lastState = store.getState();
o.observer(store);
o.triggered = false;
}, triggered);
};
private remove = (observer: Observer<Store<TStore>>) =>
this._observers.splice(
this._observers.findIndex(
o => observer === o.observer,
this._observers
),
1
);
}