-
Notifications
You must be signed in to change notification settings - Fork 0
/
create-stores.ts
106 lines (91 loc) · 3.52 KB
/
create-stores.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
import { useMemo, useRef } from 'react'
import {
InitStoresOptions,
Maybe,
SetState,
StoresApi,
StoresInitializer,
Subscriber,
getHash,
identity,
initStores,
noop,
} from 'dev-disk'
import { useSyncStoreSlice, useSyncStoresSlice } from './use-sync-store-slice.ts'
// ----------------------------------------
// Type definitions
export type UseStores<
T extends Record<string, any>,
TKey extends Record<string, any>,
TProps extends Record<string, any> = Record<string, never>,
> = {
<U = T>(...args: [Maybe<TKey>, ((state: T) => U)?] | [((state: T) => U)?]): U
} & StoresApi<T, TKey, TProps> & {
set: (key: Maybe<TKey>, value: SetState<T>) => void
get: (key?: Maybe<TKey>) => T
subscribe: (key: Maybe<TKey>, subscriber: Subscriber<T>) => () => void
getSubscribers: (key?: Maybe<TKey>) => Set<Subscriber<T>>
getInitial: (key?: Maybe<TKey>) => T
useMultiple: <U = T>(options: { keys: TKey[]; selector?: (state: T) => U }) => U[]
}
export type CreateStoresOptions<
T extends Record<string, any>,
TKey extends Record<string, any>,
> = InitStoresOptions<T, TKey> & {
onBeforeChangeKey?: (nextKey: TKey, prevKey: TKey) => void
}
// ----------------------------------------
// Source code
export const createStores = <
T extends Record<string, any>,
TKey extends Record<string, any>,
TProps extends Record<string, any> = Record<string, never>,
>(
initializer: StoresInitializer<T, TKey, TProps>,
options: CreateStoresOptions<T, TKey> = {},
): UseStores<T, TKey, TProps> => {
// prettier-ignore
const {
hashKeyFn = getHash,
onBeforeChangeKey = noop,
} = options
const defaultKey = {} as TKey
const storesApi = initStores(initializer, options)
const useStores = <U = T>(
..._args: [Maybe<TKey>, ((state: T) => U)?] | [((state: T) => U)?]
): U => {
const args = typeof _args[0] === 'function' ? [defaultKey, _args[0]] : _args
const [_key, selector = identity as (state: T) => U] = args as [Maybe<TKey>, (state: T) => U]
const key = _key || defaultKey
const keyHash = hashKeyFn(key)
const prev = useRef({ key, keyHash })
if (keyHash !== prev.current.keyHash) onBeforeChangeKey(key, prev.current.key)
prev.current = { key, keyHash }
const store = useMemo(() => storesApi.getStore(key), [keyHash])
return useSyncStoreSlice(store, selector)
}
const useMultiple = <U = T>(options: { keys: TKey[]; selector?: (state: T) => U }) => {
const { keys, selector } = options
const keyHashes = keys.map((key) => hashKeyFn(key))
const keyHashesJoined = keyHashes.join('_')
const prev = useRef({ keys, keyHashes, keyHashesJoined })
if (keyHashesJoined !== prev.current.keyHashesJoined) {
keyHashes.forEach((keyHash, i) => {
if (keyHash !== prev.current.keyHashes[i]) onBeforeChangeKey(keys[i], prev.current.keys[i])
})
}
prev.current = { keys, keyHashes, keyHashesJoined }
const stores = useMemo(() => keys.map((key) => storesApi.getStore(key)), [keyHashesJoined])
return useSyncStoresSlice(stores, selector)
}
return Object.assign(useStores, {
...storesApi,
set: (key: Maybe<TKey>, value: SetState<T>) => storesApi.getStore(key).set(value),
get: (key?: Maybe<TKey>) => storesApi.getStore(key).get(),
getInitial: (key?: Maybe<TKey>) => storesApi.getStore(key).getInitial(),
subscribe: (key: Maybe<TKey>, subscriber: Subscriber<T>) =>
storesApi.getStore(key).subscribe(subscriber),
getSubscribers: (key?: Maybe<TKey>) => storesApi.getStore(key).getSubscribers(),
useMultiple,
})
}