@tamagui/use-store 1.15.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -0
- package/dist/cjs/Store.js +99 -0
- package/dist/cjs/Store.js.map +6 -0
- package/dist/cjs/comparators.js +45 -0
- package/dist/cjs/comparators.js.map +6 -0
- package/dist/cjs/configureUseStore.js +34 -0
- package/dist/cjs/configureUseStore.js.map +6 -0
- package/dist/cjs/constants.js +35 -0
- package/dist/cjs/constants.js.map +6 -0
- package/dist/cjs/decorators.js +34 -0
- package/dist/cjs/decorators.js.map +6 -0
- package/dist/cjs/fastCompare.js +168 -0
- package/dist/cjs/fastCompare.js.map +6 -0
- package/dist/cjs/helpers.js +101 -0
- package/dist/cjs/helpers.js.map +6 -0
- package/dist/cjs/index.js +47 -0
- package/dist/cjs/index.js.map +6 -0
- package/dist/cjs/interfaces.js +17 -0
- package/dist/cjs/interfaces.js.map +6 -0
- package/dist/cjs/reaction.js +78 -0
- package/dist/cjs/reaction.js.map +6 -0
- package/dist/cjs/selector.js +139 -0
- package/dist/cjs/selector.js.map +6 -0
- package/dist/cjs/useStore.js +534 -0
- package/dist/cjs/useStore.js.map +6 -0
- package/dist/cjs/useStoreDebug.js +73 -0
- package/dist/cjs/useStoreDebug.js.map +6 -0
- package/dist/esm/Store.js +69 -0
- package/dist/esm/Store.js.map +6 -0
- package/dist/esm/Store.mjs +69 -0
- package/dist/esm/Store.mjs.map +6 -0
- package/dist/esm/comparators.js +21 -0
- package/dist/esm/comparators.js.map +6 -0
- package/dist/esm/comparators.mjs +21 -0
- package/dist/esm/comparators.mjs.map +6 -0
- package/dist/esm/configureUseStore.js +9 -0
- package/dist/esm/configureUseStore.js.map +6 -0
- package/dist/esm/configureUseStore.mjs +9 -0
- package/dist/esm/configureUseStore.mjs.map +6 -0
- package/dist/esm/constants.js +10 -0
- package/dist/esm/constants.js.map +6 -0
- package/dist/esm/constants.mjs +10 -0
- package/dist/esm/constants.mjs.map +6 -0
- package/dist/esm/decorators.js +10 -0
- package/dist/esm/decorators.js.map +6 -0
- package/dist/esm/decorators.mjs +10 -0
- package/dist/esm/decorators.mjs.map +6 -0
- package/dist/esm/fastCompare.js +143 -0
- package/dist/esm/fastCompare.js.map +6 -0
- package/dist/esm/fastCompare.mjs +143 -0
- package/dist/esm/fastCompare.mjs.map +6 -0
- package/dist/esm/helpers.js +70 -0
- package/dist/esm/helpers.js.map +6 -0
- package/dist/esm/helpers.mjs +70 -0
- package/dist/esm/helpers.mjs.map +6 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/index.js.map +6 -0
- package/dist/esm/index.mjs +14 -0
- package/dist/esm/index.mjs.map +6 -0
- package/dist/esm/interfaces.js +1 -0
- package/dist/esm/interfaces.js.map +6 -0
- package/dist/esm/interfaces.mjs +1 -0
- package/dist/esm/interfaces.mjs.map +6 -0
- package/dist/esm/reaction.js +53 -0
- package/dist/esm/reaction.js.map +6 -0
- package/dist/esm/reaction.mjs +53 -0
- package/dist/esm/reaction.mjs.map +6 -0
- package/dist/esm/selector.js +114 -0
- package/dist/esm/selector.js.map +6 -0
- package/dist/esm/selector.mjs +114 -0
- package/dist/esm/selector.mjs.map +6 -0
- package/dist/esm/useStore.js +516 -0
- package/dist/esm/useStore.js.map +6 -0
- package/dist/esm/useStore.mjs +516 -0
- package/dist/esm/useStore.mjs.map +6 -0
- package/dist/esm/useStoreDebug.js +35 -0
- package/dist/esm/useStoreDebug.js.map +6 -0
- package/dist/esm/useStoreDebug.mjs +35 -0
- package/dist/esm/useStoreDebug.mjs.map +6 -0
- package/package.json +44 -0
- package/src/Store.tsx +77 -0
- package/src/comparators.tsx +18 -0
- package/src/configureUseStore.tsx +7 -0
- package/src/constants.tsx +6 -0
- package/src/decorators.tsx +8 -0
- package/src/helpers.tsx +87 -0
- package/src/index.ts +9 -0
- package/src/interfaces.tsx +32 -0
- package/src/reaction.tsx +109 -0
- package/src/selector.tsx +129 -0
- package/src/useStore.tsx +705 -0
- package/src/useStoreDebug.tsx +40 -0
- package/types/Store.d.ts +28 -0
- package/types/Store.d.ts.map +1 -0
- package/types/comparators.d.ts +6 -0
- package/types/comparators.d.ts.map +1 -0
- package/types/configureUseStore.d.ts +4 -0
- package/types/configureUseStore.d.ts.map +1 -0
- package/types/constants.d.ts +6 -0
- package/types/constants.d.ts.map +1 -0
- package/types/decorators.d.ts +3 -0
- package/types/decorators.d.ts.map +1 -0
- package/types/fastCompare.d.ts +13 -0
- package/types/fastCompare.d.ts.map +1 -0
- package/types/helpers.d.ts +13 -0
- package/types/helpers.d.ts.map +1 -0
- package/types/index.d.ts +10 -0
- package/types/index.d.ts.map +1 -0
- package/types/interfaces.d.ts +30 -0
- package/types/interfaces.d.ts.map +1 -0
- package/types/reaction.d.ts +4 -0
- package/types/reaction.d.ts.map +1 -0
- package/types/selector.d.ts +3 -0
- package/types/selector.d.ts.map +1 -0
- package/types/useStore.d.ts +18 -0
- package/types/useStore.d.ts.map +1 -0
- package/types/useStoreDebug.d.ts +7 -0
- package/types/useStoreDebug.d.ts.map +1 -0
package/src/Store.tsx
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { UNWRAP_PROXY } from './constants'
|
|
2
|
+
import { shouldDebug } from './useStoreDebug'
|
|
3
|
+
|
|
4
|
+
export const TRIGGER_UPDATE = Symbol()
|
|
5
|
+
export const ADD_TRACKER = Symbol()
|
|
6
|
+
export const TRACK = Symbol()
|
|
7
|
+
export const SHOULD_DEBUG = Symbol()
|
|
8
|
+
|
|
9
|
+
export type StoreTracker = {
|
|
10
|
+
isTracking: boolean
|
|
11
|
+
tracked: Set<string>
|
|
12
|
+
dispose: () => void
|
|
13
|
+
component?: any
|
|
14
|
+
firstRun: boolean
|
|
15
|
+
last?: any
|
|
16
|
+
lastKeys?: any
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const disableTracking = new WeakMap()
|
|
20
|
+
|
|
21
|
+
export const setDisableStoreTracking = (storeInstance: any, val: boolean) => {
|
|
22
|
+
const store = storeInstance[UNWRAP_PROXY] ?? storeInstance
|
|
23
|
+
disableTracking.set(store, val)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class Store<Props extends Object = {}> {
|
|
27
|
+
private _listeners = new Set<Function>()
|
|
28
|
+
private _trackers = new Set<StoreTracker>()
|
|
29
|
+
_version = 0
|
|
30
|
+
|
|
31
|
+
constructor(public props: Props) {}
|
|
32
|
+
|
|
33
|
+
subscribe = (onChanged: Function) => {
|
|
34
|
+
this._listeners.add(onChanged)
|
|
35
|
+
return () => {
|
|
36
|
+
this._listeners.delete(onChanged)
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
[TRIGGER_UPDATE]() {
|
|
41
|
+
this._version = (this._version + 1) % Number.MAX_SAFE_INTEGER
|
|
42
|
+
for (const cb of this._listeners) {
|
|
43
|
+
cb()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
[ADD_TRACKER](tracker: StoreTracker) {
|
|
48
|
+
this._trackers.add(tracker)
|
|
49
|
+
return () => {
|
|
50
|
+
this._trackers.delete(tracker)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
[TRACK](key: string, debug?: boolean) {
|
|
55
|
+
if (key[0] === '_' || key[0] === '$' || key === 'props' || key === 'toJSON') {
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
if (debug) {
|
|
59
|
+
console.log('(debug) CHECK TRACKERS FOR', key)
|
|
60
|
+
}
|
|
61
|
+
for (const tracker of this._trackers) {
|
|
62
|
+
if (tracker.isTracking) {
|
|
63
|
+
tracker.tracked.add(key)
|
|
64
|
+
if (debug) {
|
|
65
|
+
console.log('(debug) TRACK', key, tracker)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
[SHOULD_DEBUG]() {
|
|
72
|
+
const info = { storeInstance: this }
|
|
73
|
+
return [...this._trackers].some(
|
|
74
|
+
(tracker) => tracker.component && shouldDebug(tracker.component, info)
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const isEqualSubsetShallow = (
|
|
2
|
+
a: any,
|
|
3
|
+
b: any,
|
|
4
|
+
opts?: { keyComparators?: { [key: string]: (a: any, b: any) => boolean } }
|
|
5
|
+
) => {
|
|
6
|
+
if (b == null || a == null) return a === b
|
|
7
|
+
if (typeof a !== typeof b) return false
|
|
8
|
+
if (typeof b === 'object') {
|
|
9
|
+
for (const key in b) {
|
|
10
|
+
const compare = opts?.keyComparators?.[key]
|
|
11
|
+
if (compare ? !compare(a[key], b[key]) : b[key] !== a[key]) {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return true
|
|
16
|
+
}
|
|
17
|
+
return a === b
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ComparisonFn = (a: any, b: any) => boolean
|
|
2
|
+
|
|
3
|
+
export function compare(comparator: ComparisonFn) {
|
|
4
|
+
return (target: any, propertyKey: string): any => {
|
|
5
|
+
target['_comparators'] = target['_comparators'] || {}
|
|
6
|
+
target['_comparators'][propertyKey] = comparator
|
|
7
|
+
}
|
|
8
|
+
}
|
package/src/helpers.tsx
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
import { StoreInfo } from './interfaces'
|
|
4
|
+
|
|
5
|
+
const wkm = new WeakMap<any, string>()
|
|
6
|
+
const weakKey = (obj: any, prefix = '') => {
|
|
7
|
+
if (wkm.has(obj)) return wkm.get(obj)!
|
|
8
|
+
const key = `${prefix}-${Math.random()}`
|
|
9
|
+
wkm.set(obj, key)
|
|
10
|
+
return key
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getStoreUid(Constructor: any, props: string | Object | void) {
|
|
14
|
+
// in dev mode we can use name which gives us nice `allStores.StoreName` access
|
|
15
|
+
// in prod mode it usually is minified and mangled, unsafe to use name so use weakkey
|
|
16
|
+
const storeName =
|
|
17
|
+
process.env.NODE_ENV === 'development' ? Constructor.name : weakKey(Constructor)
|
|
18
|
+
return `${storeName}${!props ? '' : typeof props === 'string' ? props : getKey(props)}`
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const UNWRAP_STORE_INFO = Symbol('UNWRAP_STORE_INFO')
|
|
22
|
+
export const cache = new Map<string, StoreInfo>()
|
|
23
|
+
|
|
24
|
+
export function getStoreDescriptors(storeInstance: any) {
|
|
25
|
+
const proto = Object.getPrototypeOf(storeInstance)
|
|
26
|
+
const instanceDescriptors = Object.getOwnPropertyDescriptors(storeInstance)
|
|
27
|
+
const protoDescriptors = Object.getOwnPropertyDescriptors(proto)
|
|
28
|
+
const descriptors = {
|
|
29
|
+
...protoDescriptors,
|
|
30
|
+
...instanceDescriptors,
|
|
31
|
+
}
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
delete descriptors.constructor
|
|
34
|
+
return descriptors
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function get<A>(_: A, b?: any): A extends new (props?: any) => infer B ? B : A {
|
|
38
|
+
return _ as any
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function getKey(props: Object) {
|
|
42
|
+
let s = ''
|
|
43
|
+
const sorted = Object.keys(props).sort()
|
|
44
|
+
for (const key of sorted) {
|
|
45
|
+
const v = props[key]
|
|
46
|
+
if (v && typeof v === 'object') {
|
|
47
|
+
s += getKey(v)
|
|
48
|
+
} else {
|
|
49
|
+
s += `.${key}:${v}`
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return s
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
type ResultBox<T> = { v: T }
|
|
56
|
+
|
|
57
|
+
export default function useConstant<T>(fn: () => T): T {
|
|
58
|
+
const ref = useRef<ResultBox<T>>()
|
|
59
|
+
if (!ref.current) {
|
|
60
|
+
ref.current = { v: fn() }
|
|
61
|
+
}
|
|
62
|
+
return ref.current.v
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function simpleStr(arg: any) {
|
|
66
|
+
if (process.env.NODE_ENV === 'development') {
|
|
67
|
+
return typeof arg === 'function'
|
|
68
|
+
? 'fn'
|
|
69
|
+
: typeof arg === 'string'
|
|
70
|
+
? `"${arg}"`
|
|
71
|
+
: !arg
|
|
72
|
+
? arg
|
|
73
|
+
: typeof arg !== 'object'
|
|
74
|
+
? arg
|
|
75
|
+
: Array.isArray(arg)
|
|
76
|
+
? '[...]'
|
|
77
|
+
: `{...}`
|
|
78
|
+
}
|
|
79
|
+
return arg
|
|
80
|
+
}
|
|
81
|
+
// helper for debugging
|
|
82
|
+
|
|
83
|
+
export function getStoreDebugInfo(store: any) {
|
|
84
|
+
return (
|
|
85
|
+
store[UNWRAP_STORE_INFO] ?? cache.get(getStoreUid(store.constructor, store.props))
|
|
86
|
+
)
|
|
87
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './useStore'
|
|
2
|
+
export { configureUseStore } from './configureUseStore'
|
|
3
|
+
export * from './interfaces'
|
|
4
|
+
export * from './selector'
|
|
5
|
+
export * from './reaction'
|
|
6
|
+
export * from './Store'
|
|
7
|
+
export { UNWRAP_PROXY } from './constants'
|
|
8
|
+
export * from './comparators'
|
|
9
|
+
export * from './decorators'
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Store } from './Store'
|
|
2
|
+
|
|
3
|
+
export type Selector<A = unknown, B = unknown> = (x: A) => B
|
|
4
|
+
|
|
5
|
+
export type UseStoreSelector<Store, Res> = (store: Store) => Res
|
|
6
|
+
export type UseStoreOptions<Store = any, SelectorRes = any> = {
|
|
7
|
+
debug?: boolean
|
|
8
|
+
selector?: UseStoreSelector<Store, SelectorRes>
|
|
9
|
+
once?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type StoreInfo<A = Store> = {
|
|
13
|
+
keyComparators?: {
|
|
14
|
+
[key: string]: (a: any, b: any) => boolean
|
|
15
|
+
}
|
|
16
|
+
// proxied store
|
|
17
|
+
store: A
|
|
18
|
+
storeInstance: any
|
|
19
|
+
getters: { [key: string]: any }
|
|
20
|
+
actions: any
|
|
21
|
+
stateKeys: string[]
|
|
22
|
+
gettersState: {
|
|
23
|
+
getCache: Map<string, any>
|
|
24
|
+
depsToGetter: Map<string, Set<string>>
|
|
25
|
+
curGetKeys: Set<string>
|
|
26
|
+
isGetting: boolean
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type UseStoreConfig = {
|
|
31
|
+
logLevel?: 'debug' | 'info' | 'error'
|
|
32
|
+
}
|
package/src/reaction.tsx
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
import { isEqualSubsetShallow } from './comparators'
|
|
4
|
+
import { UNWRAP_PROXY } from './constants'
|
|
5
|
+
import { Store } from './Store'
|
|
6
|
+
import { setIsInReaction } from './useStore'
|
|
7
|
+
|
|
8
|
+
const dispose = (d: any) => {
|
|
9
|
+
if (typeof d === 'function') {
|
|
10
|
+
d()
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useReaction<
|
|
15
|
+
StoreInstance extends Store<any>,
|
|
16
|
+
Selector extends (a: StoreInstance) => any
|
|
17
|
+
>(
|
|
18
|
+
store: StoreInstance,
|
|
19
|
+
selector: Selector,
|
|
20
|
+
receiver: Selector extends (a: StoreInstance) => infer Derived
|
|
21
|
+
? (a: Derived) => any
|
|
22
|
+
: unknown,
|
|
23
|
+
equalityFn: (a: any, b: any) => boolean = isEqualSubsetShallow,
|
|
24
|
+
memoArgs?: any[]
|
|
25
|
+
) {
|
|
26
|
+
return useMemo(() => reaction(store, selector, receiver, equalityFn), [memoArgs])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function reaction<
|
|
30
|
+
StoreInstance extends Store<any>,
|
|
31
|
+
Selector extends (a: StoreInstance) => any
|
|
32
|
+
>(
|
|
33
|
+
store: StoreInstance,
|
|
34
|
+
selector: Selector,
|
|
35
|
+
receiver: Selector extends (a: StoreInstance) => infer Derived
|
|
36
|
+
? (a: Derived) => any
|
|
37
|
+
: unknown,
|
|
38
|
+
equalityFn: (a: any, b: any) => boolean = isEqualSubsetShallow
|
|
39
|
+
) {
|
|
40
|
+
let last: any = undefined
|
|
41
|
+
let innerDispose: any
|
|
42
|
+
|
|
43
|
+
function updateReaction() {
|
|
44
|
+
try {
|
|
45
|
+
setIsInReaction(true)
|
|
46
|
+
const storeInstance = store[UNWRAP_PROXY] || store
|
|
47
|
+
const next = selector(storeInstance)
|
|
48
|
+
if (!equalityFn(last, next)) {
|
|
49
|
+
if (process.env.NODE_ENV === 'development') {
|
|
50
|
+
console.groupCollapsed(
|
|
51
|
+
`🌑 ⏭ %c${receiver.name.padStart(24)} (${storeInstance.constructor.name}${
|
|
52
|
+
store.props?.id ? `:${store.props.id}` : ''
|
|
53
|
+
}) ${last} => ${next}`,
|
|
54
|
+
'color: chocolate;'
|
|
55
|
+
)
|
|
56
|
+
console.groupCollapsed('trace >')
|
|
57
|
+
console.trace()
|
|
58
|
+
console.groupEnd()
|
|
59
|
+
console.log(' ARG', next)
|
|
60
|
+
console.groupEnd()
|
|
61
|
+
}
|
|
62
|
+
dispose(innerDispose)
|
|
63
|
+
last = next
|
|
64
|
+
innerDispose = receiver(next)
|
|
65
|
+
}
|
|
66
|
+
} finally {
|
|
67
|
+
setIsInReaction(false)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const disposeSubscribe = store.subscribe(updateReaction)
|
|
72
|
+
updateReaction()
|
|
73
|
+
|
|
74
|
+
return () => {
|
|
75
|
+
disposeSubscribe()
|
|
76
|
+
dispose(innerDispose)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// start on simpler reaction
|
|
81
|
+
// export function reaction2(fn: () => any): () => void {
|
|
82
|
+
// let state = runStoreSelector(fn)
|
|
83
|
+
// let disposeSubscribe
|
|
84
|
+
// const disposePrev = () => {
|
|
85
|
+
// // treat return functions as dispose
|
|
86
|
+
// if (typeof state.value === 'function') {
|
|
87
|
+
// state.value()
|
|
88
|
+
// }
|
|
89
|
+
// }
|
|
90
|
+
// const dispose = () => {
|
|
91
|
+
// disposeSubscribe?.()
|
|
92
|
+
// disposePrev()
|
|
93
|
+
// }
|
|
94
|
+
// function update() {
|
|
95
|
+
// dispose()
|
|
96
|
+
// disposeSubscribe = subscribeToStores([...state.stores], () => {
|
|
97
|
+
// const next = runStoreSelector(fn)
|
|
98
|
+
// disposePrev()
|
|
99
|
+
// if (!isEqualSubsetShallow(state.stores, next.stores)) {
|
|
100
|
+
// state = next
|
|
101
|
+
// update()
|
|
102
|
+
// } else {
|
|
103
|
+
// state = next
|
|
104
|
+
// }
|
|
105
|
+
// })
|
|
106
|
+
// }
|
|
107
|
+
// update()
|
|
108
|
+
// return dispose
|
|
109
|
+
// }
|
package/src/selector.tsx
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { isEqualSubsetShallow } from './comparators'
|
|
4
|
+
import { UNWRAP_PROXY } from './constants'
|
|
5
|
+
import { setIsInReaction, trackStoresAccess } from './useStore'
|
|
6
|
+
|
|
7
|
+
// TODO i think we can just replace reaction() with this, its not worse in any way
|
|
8
|
+
|
|
9
|
+
const logUpdate =
|
|
10
|
+
process.env.NODE_ENV === 'development'
|
|
11
|
+
? (fn: any, stores: any[], last: any, next: any) => {
|
|
12
|
+
const getStoreLogName = (store: any) => {
|
|
13
|
+
const str = store[UNWRAP_PROXY] ?? store
|
|
14
|
+
return `${str.constructor.name}${store.props?.id ? `:${store.props.id}` : ''}`
|
|
15
|
+
}
|
|
16
|
+
const storeNames = stores.map(getStoreLogName).join(', ')
|
|
17
|
+
const name = `🌑 ▶️ %c${fn.name} ${storeNames} () ${last} => ${next}`
|
|
18
|
+
console.groupCollapsed(name, 'color: tomato;')
|
|
19
|
+
console.groupCollapsed('trace >')
|
|
20
|
+
console.trace()
|
|
21
|
+
console.groupEnd()
|
|
22
|
+
console.log(' next', next)
|
|
23
|
+
console.groupEnd()
|
|
24
|
+
}
|
|
25
|
+
: null
|
|
26
|
+
|
|
27
|
+
// TODO test this works the same as useSelector
|
|
28
|
+
export function selector(fn: () => any) {
|
|
29
|
+
let prev = runStoreSelector(fn)
|
|
30
|
+
let disposeValue: Function | null = null
|
|
31
|
+
const subscribe = () => {
|
|
32
|
+
return subscribeToStores([...prev.stores], () => {
|
|
33
|
+
try {
|
|
34
|
+
disposeValue?.()
|
|
35
|
+
setIsInReaction(true)
|
|
36
|
+
const next = runStoreSelector(fn)
|
|
37
|
+
if (typeof next.value === 'function') {
|
|
38
|
+
disposeValue = next.value
|
|
39
|
+
if (process.env.NODE_ENV === 'development') {
|
|
40
|
+
logUpdate!(fn, [...next.stores], '(fn)', '(fn)')
|
|
41
|
+
}
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
if (
|
|
45
|
+
isEqualSubsetShallow(prev.stores, next.stores) &&
|
|
46
|
+
isEqualSubsetShallow(prev.value, next.value)
|
|
47
|
+
) {
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
if (process.env.NODE_ENV === 'development') {
|
|
51
|
+
logUpdate!(fn, [...next.stores], prev.value, next.value)
|
|
52
|
+
}
|
|
53
|
+
prev = next
|
|
54
|
+
dispose()
|
|
55
|
+
dispose = subscribe()
|
|
56
|
+
} finally {
|
|
57
|
+
setIsInReaction(false)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
let dispose = subscribe()
|
|
62
|
+
return () => {
|
|
63
|
+
dispose()
|
|
64
|
+
disposeValue?.()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function useSelector<A>(fn: () => A): A {
|
|
69
|
+
const [state, setState] = useState(() => {
|
|
70
|
+
return runStoreSelector(fn)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
let dispose
|
|
75
|
+
const unsub = subscribeToStores([...state.stores], () => {
|
|
76
|
+
dispose?.()
|
|
77
|
+
const next = runStoreSelector(fn)
|
|
78
|
+
// return function === return disposable
|
|
79
|
+
if (typeof next.value === 'function') {
|
|
80
|
+
if (process.env.NODE_ENV === 'development') {
|
|
81
|
+
logUpdate!(fn, [...next.stores], '(fn)', '(fn)')
|
|
82
|
+
}
|
|
83
|
+
dispose = next.value
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
setState((prev) => {
|
|
87
|
+
if (
|
|
88
|
+
isEqualSubsetShallow(prev.stores, next.stores) &&
|
|
89
|
+
isEqualSubsetShallow(prev.value, next.value)
|
|
90
|
+
) {
|
|
91
|
+
return prev
|
|
92
|
+
}
|
|
93
|
+
if (process.env.NODE_ENV === 'development') {
|
|
94
|
+
logUpdate!(fn, [...next.stores], prev.value, next.value)
|
|
95
|
+
}
|
|
96
|
+
return next
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
return () => {
|
|
100
|
+
unsub()
|
|
101
|
+
dispose?.()
|
|
102
|
+
}
|
|
103
|
+
}, [...state.stores])
|
|
104
|
+
|
|
105
|
+
return state.value
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function runStoreSelector<A>(selector: () => A): { value: A; stores: Set<any> } {
|
|
109
|
+
const stores = new Set()
|
|
110
|
+
const dispose = trackStoresAccess((store) => {
|
|
111
|
+
stores.add(store)
|
|
112
|
+
})
|
|
113
|
+
const value = selector()
|
|
114
|
+
dispose()
|
|
115
|
+
return {
|
|
116
|
+
value,
|
|
117
|
+
stores,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function subscribeToStores(stores: any[], onUpdate: () => any) {
|
|
122
|
+
const disposes: Function[] = []
|
|
123
|
+
for (const store of stores) {
|
|
124
|
+
disposes.push(store.subscribe(onUpdate))
|
|
125
|
+
}
|
|
126
|
+
return () => {
|
|
127
|
+
disposes.forEach((x) => x())
|
|
128
|
+
}
|
|
129
|
+
}
|