-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstore.ts
More file actions
107 lines (87 loc) · 3.12 KB
/
store.ts
File metadata and controls
107 lines (87 loc) · 3.12 KB
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
import arachnea from "arachnea";
import { type Subscriber, type SetterFunc, SET_STATE } from "./types";
import { generateRandomString } from "./utils";
export class Store<State> {
private state: State
private subscriptionMap: Record<string, Array<string>>;
private lastSubscriber: string;
private subscribers: Record<string, Subscriber<State>>;
private mounted: boolean;
private mountedId: string;
constructor(state: State) {
this.state = state;
this.subscribers = {};
this.subscriptionMap = {};
this.lastSubscriber = "";
this.mounted = false;
this.mountedId = "";
}
public get() {
return this.state;
}
public subscribe(eventName: string, subscriber: Subscriber<State>) {
if (!Object.keys(this.subscriptionMap).includes(eventName)) {
this.subscriptionMap[eventName] = []
}
const id = generateRandomString(100);
this.subscriptionMap[eventName].push(id);
this.subscribers[id] = subscriber;
this.lastSubscriber = id;
return this;
}
public getLastSubscriberId(): string {
return this.lastSubscriber;
}
public unsubscribe(eventName: string, id: string) {
if (!Object.keys(this.subscriptionMap).includes(eventName)) {
return this;
}
this.subscriptionMap[eventName] = arachnea(this.subscriptionMap[eventName])
.remove(id)
.collect();
delete this.subscribers[id];
return this;
}
public dispatch(eventName: string) {
if (!this.subscriptionMap[eventName]) {
return this;
}
arachnea(this.subscriptionMap[eventName])
.forEach(id => this.subscribers[id](this.get()))
.collect();
return this;
}
public set(newValue: SetterFunc<State>) {
let nextState: State;
if (typeof newValue === 'function') {
nextState = { ...this.state, ...newValue(this.state) };
} else {
nextState = { ...this.state, ...newValue };
}
if (!this.shallowEqual(this.state, nextState)) {
this.state = nextState;
return this.dispatch(SET_STATE);
}
return this;
}
private shallowEqual(obj1: any, obj2: any): boolean {
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) return false;
for (let key of keys1) {
if (obj1[key] !== obj2[key]) return false;
}
return true;
}
public onSetState(subscriber: Subscriber<State>) {
return this.subscribe(SET_STATE, subscriber);
}
public attachComponent(subscriber: Subscriber<State>) {
this.mountedId = this.subscribe(SET_STATE, subscriber).getLastSubscriberId();
return this;
}
public detachComponent() {
return this.unsubscribe(SET_STATE, this.mountedId);
}
}
export const createStore = <State>(initialState: State) => new Store(initialState);