-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuseState.ts
87 lines (69 loc) · 2.11 KB
/
useState.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
import { Ref, ref } from "vue";
const changeEvent = "C";
export type State = any;
export class Action<T extends any = any> {
static readonly type: string;
readonly payload: T;
constructor(payload?: T) {
if (payload !== undefined) {
this.payload = payload;
}
}
}
export type Reducer<S extends State, A extends Action> = (
state: S,
action: A
) => S | Promise<S> | void | Promise<void>;
export type Selector<T extends State, U extends any> = (state: T) => U;
class SetterAction<S extends State = any> extends Action<{
key: keyof S;
value: S[keyof S];
}> {
static readonly type = ":set:";
constructor(key: keyof S, value: S[keyof S]) {
super({ key, value });
}
}
function setterReducer<S extends State>(state: S, action: SetterAction<S>) {
return Object.assign({}, state, {
[action.payload.key]: action.payload.value,
});
}
export function useState<StateType extends State>(initial: StateType) {
let state: StateType = initial;
const events = new EventTarget();
const reducers: Array<{ type: string; R: Reducer<StateType, any> }> = [];
function select<U extends any>(f: Selector<StateType, U>) {
const v = ref<any>(f(state));
events.addEventListener(changeEvent, () => {
const newValue = f(state);
if (v.value !== newValue) {
v.value = newValue;
}
});
return v as Ref<U>;
}
function reduce<
A extends typeof Action<any>,
R extends Reducer<StateType, InstanceType<A>>
>(action: A, reducer: R) {
reducers.push({ type: action.type, R: reducer });
}
async function dispatch<A extends Action>(action: A) {
const type: string = Object.getPrototypeOf(action).constructor.type;
for (const reducer of reducers) {
if (reducer.type === type) {
state = (await reducer.R(state, action)) || state;
}
}
events.dispatchEvent(new CustomEvent(changeEvent));
}
function set<K extends keyof StateType, V extends StateType[K]>(
key: K,
value: V
) {
dispatch(new SetterAction<StateType>(key, value));
}
reduce(SetterAction as any, setterReducer);
return { select, dispatch, reduce, set };
}