@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.
Files changed (118) hide show
  1. package/README.md +91 -0
  2. package/dist/cjs/Store.js +99 -0
  3. package/dist/cjs/Store.js.map +6 -0
  4. package/dist/cjs/comparators.js +45 -0
  5. package/dist/cjs/comparators.js.map +6 -0
  6. package/dist/cjs/configureUseStore.js +34 -0
  7. package/dist/cjs/configureUseStore.js.map +6 -0
  8. package/dist/cjs/constants.js +35 -0
  9. package/dist/cjs/constants.js.map +6 -0
  10. package/dist/cjs/decorators.js +34 -0
  11. package/dist/cjs/decorators.js.map +6 -0
  12. package/dist/cjs/fastCompare.js +168 -0
  13. package/dist/cjs/fastCompare.js.map +6 -0
  14. package/dist/cjs/helpers.js +101 -0
  15. package/dist/cjs/helpers.js.map +6 -0
  16. package/dist/cjs/index.js +47 -0
  17. package/dist/cjs/index.js.map +6 -0
  18. package/dist/cjs/interfaces.js +17 -0
  19. package/dist/cjs/interfaces.js.map +6 -0
  20. package/dist/cjs/reaction.js +78 -0
  21. package/dist/cjs/reaction.js.map +6 -0
  22. package/dist/cjs/selector.js +139 -0
  23. package/dist/cjs/selector.js.map +6 -0
  24. package/dist/cjs/useStore.js +534 -0
  25. package/dist/cjs/useStore.js.map +6 -0
  26. package/dist/cjs/useStoreDebug.js +73 -0
  27. package/dist/cjs/useStoreDebug.js.map +6 -0
  28. package/dist/esm/Store.js +69 -0
  29. package/dist/esm/Store.js.map +6 -0
  30. package/dist/esm/Store.mjs +69 -0
  31. package/dist/esm/Store.mjs.map +6 -0
  32. package/dist/esm/comparators.js +21 -0
  33. package/dist/esm/comparators.js.map +6 -0
  34. package/dist/esm/comparators.mjs +21 -0
  35. package/dist/esm/comparators.mjs.map +6 -0
  36. package/dist/esm/configureUseStore.js +9 -0
  37. package/dist/esm/configureUseStore.js.map +6 -0
  38. package/dist/esm/configureUseStore.mjs +9 -0
  39. package/dist/esm/configureUseStore.mjs.map +6 -0
  40. package/dist/esm/constants.js +10 -0
  41. package/dist/esm/constants.js.map +6 -0
  42. package/dist/esm/constants.mjs +10 -0
  43. package/dist/esm/constants.mjs.map +6 -0
  44. package/dist/esm/decorators.js +10 -0
  45. package/dist/esm/decorators.js.map +6 -0
  46. package/dist/esm/decorators.mjs +10 -0
  47. package/dist/esm/decorators.mjs.map +6 -0
  48. package/dist/esm/fastCompare.js +143 -0
  49. package/dist/esm/fastCompare.js.map +6 -0
  50. package/dist/esm/fastCompare.mjs +143 -0
  51. package/dist/esm/fastCompare.mjs.map +6 -0
  52. package/dist/esm/helpers.js +70 -0
  53. package/dist/esm/helpers.js.map +6 -0
  54. package/dist/esm/helpers.mjs +70 -0
  55. package/dist/esm/helpers.mjs.map +6 -0
  56. package/dist/esm/index.js +14 -0
  57. package/dist/esm/index.js.map +6 -0
  58. package/dist/esm/index.mjs +14 -0
  59. package/dist/esm/index.mjs.map +6 -0
  60. package/dist/esm/interfaces.js +1 -0
  61. package/dist/esm/interfaces.js.map +6 -0
  62. package/dist/esm/interfaces.mjs +1 -0
  63. package/dist/esm/interfaces.mjs.map +6 -0
  64. package/dist/esm/reaction.js +53 -0
  65. package/dist/esm/reaction.js.map +6 -0
  66. package/dist/esm/reaction.mjs +53 -0
  67. package/dist/esm/reaction.mjs.map +6 -0
  68. package/dist/esm/selector.js +114 -0
  69. package/dist/esm/selector.js.map +6 -0
  70. package/dist/esm/selector.mjs +114 -0
  71. package/dist/esm/selector.mjs.map +6 -0
  72. package/dist/esm/useStore.js +516 -0
  73. package/dist/esm/useStore.js.map +6 -0
  74. package/dist/esm/useStore.mjs +516 -0
  75. package/dist/esm/useStore.mjs.map +6 -0
  76. package/dist/esm/useStoreDebug.js +35 -0
  77. package/dist/esm/useStoreDebug.js.map +6 -0
  78. package/dist/esm/useStoreDebug.mjs +35 -0
  79. package/dist/esm/useStoreDebug.mjs.map +6 -0
  80. package/package.json +44 -0
  81. package/src/Store.tsx +77 -0
  82. package/src/comparators.tsx +18 -0
  83. package/src/configureUseStore.tsx +7 -0
  84. package/src/constants.tsx +6 -0
  85. package/src/decorators.tsx +8 -0
  86. package/src/helpers.tsx +87 -0
  87. package/src/index.ts +9 -0
  88. package/src/interfaces.tsx +32 -0
  89. package/src/reaction.tsx +109 -0
  90. package/src/selector.tsx +129 -0
  91. package/src/useStore.tsx +705 -0
  92. package/src/useStoreDebug.tsx +40 -0
  93. package/types/Store.d.ts +28 -0
  94. package/types/Store.d.ts.map +1 -0
  95. package/types/comparators.d.ts +6 -0
  96. package/types/comparators.d.ts.map +1 -0
  97. package/types/configureUseStore.d.ts +4 -0
  98. package/types/configureUseStore.d.ts.map +1 -0
  99. package/types/constants.d.ts +6 -0
  100. package/types/constants.d.ts.map +1 -0
  101. package/types/decorators.d.ts +3 -0
  102. package/types/decorators.d.ts.map +1 -0
  103. package/types/fastCompare.d.ts +13 -0
  104. package/types/fastCompare.d.ts.map +1 -0
  105. package/types/helpers.d.ts +13 -0
  106. package/types/helpers.d.ts.map +1 -0
  107. package/types/index.d.ts +10 -0
  108. package/types/index.d.ts.map +1 -0
  109. package/types/interfaces.d.ts +30 -0
  110. package/types/interfaces.d.ts.map +1 -0
  111. package/types/reaction.d.ts +4 -0
  112. package/types/reaction.d.ts.map +1 -0
  113. package/types/selector.d.ts +3 -0
  114. package/types/selector.d.ts.map +1 -0
  115. package/types/useStore.d.ts +18 -0
  116. package/types/useStore.d.ts.map +1 -0
  117. package/types/useStoreDebug.d.ts +7 -0
  118. 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,7 @@
1
+ import { UseStoreConfig } from './interfaces'
2
+
3
+ export let configureOpts: UseStoreConfig = {}
4
+
5
+ export function configureUseStore(opts: UseStoreConfig) {
6
+ configureOpts = opts
7
+ }
@@ -0,0 +1,6 @@
1
+ export const UNWRAP_PROXY = Symbol()
2
+
3
+ export const defaultOptions = {
4
+ once: false,
5
+ selector: undefined,
6
+ }
@@ -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
+ }
@@ -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
+ }
@@ -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
+ // }
@@ -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
+ }