-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstore.ts
78 lines (65 loc) · 2.27 KB
/
store.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
import { Maybe, getValue, noop } from './utils.ts'
// ----------------------------------------
// Type definitions
export type SetState<T> = Partial<T> | ((state: T) => Partial<T>)
export type Subscriber<T> = (state: T, prevState: T) => void
export type StoreApi<T extends Record<string, any>> = {
set: (value: SetState<T>) => void
get: () => T
getInitial: () => T
subscribe: (subscriber: Subscriber<T>) => () => void
getSubscribers: () => Set<Subscriber<T>>
}
export type StoreInitializer<
T extends Record<string, any>,
TProps extends Record<string, any> = Record<string, never>,
> = T | ((store: StoreApi<T> & TProps) => T)
export type InitStoreOptions<T extends Record<string, any>> = {
intercept?: (nextState: T, prevState: T) => void | Maybe<Partial<T>>
onFirstSubscribe?: (state: T) => void
onSubscribe?: (state: T) => void
onUnsubscribe?: (state: T) => void
onLastUnsubscribe?: (state: T) => void
}
// ----------------------------------------
// Source code
export const initStore = <
T extends Record<string, any>,
TProps extends Record<string, any> = Record<string, never>,
>(
initializer: StoreInitializer<T, TProps>,
options: InitStoreOptions<T> = {},
): StoreApi<T> & TProps => {
const {
intercept,
onFirstSubscribe = noop,
onSubscribe = noop,
onUnsubscribe = noop,
onLastUnsubscribe = noop,
} = options
let state: T
const get = () => state
const getInitial = () => initialState
const subscribers = new Set<Subscriber<T>>()
const getSubscribers = () => subscribers
const set = (value: SetState<T>) => {
const prevState = state
state = { ...state, ...getValue(value, state) }
if (intercept) state = { ...state, ...intercept(state, prevState) }
subscribers.forEach((subscriber) => subscriber(state, prevState))
}
const subscribe = (subscriber: Subscriber<T>) => {
subscribers.add(subscriber)
if (subscribers.size === 1) onFirstSubscribe(state)
onSubscribe(state)
return () => {
subscribers.delete(subscriber)
onUnsubscribe(state)
if (subscribers.size === 0) onLastUnsubscribe(state)
}
}
const store = { set, get, getInitial, subscribe, getSubscribers } as StoreApi<T> & TProps
const initialState: T = getValue(initializer, store)
state = initialState
return store
}