@legendapp/state 2.2.0-next.7 → 2.2.0-next.71

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 (298) hide show
  1. package/README.md +4 -2
  2. package/babel.js.map +1 -1
  3. package/config/enable$get.d.ts +8 -0
  4. package/config/enable$get.js +24 -0
  5. package/config/enable$get.js.map +1 -0
  6. package/config/enable$get.mjs +21 -0
  7. package/config/enable$get.mjs.map +1 -0
  8. package/config/enableReactComponents.js.map +1 -1
  9. package/config/enableReactComponents.mjs.map +1 -1
  10. package/config/enableReactNativeComponents.js.map +1 -1
  11. package/config/enableReactNativeComponents.mjs.map +1 -1
  12. package/config/enableReactTracking.d.ts +0 -9
  13. package/config/enableReactTracking.js.map +1 -1
  14. package/config/enableReactTracking.mjs.map +1 -1
  15. package/config/enableReactUse.d.ts +1 -1
  16. package/config/enableReactUse.js +1 -0
  17. package/config/enableReactUse.js.map +1 -1
  18. package/config/enableReactUse.mjs +1 -0
  19. package/config/enableReactUse.mjs.map +1 -1
  20. package/config/enable_peek.d.ts +8 -0
  21. package/config/{enableDirectPeek.js → enable_peek.js} +6 -3
  22. package/config/enable_peek.js.map +1 -0
  23. package/config/{enableDirectPeek.mjs → enable_peek.mjs} +5 -3
  24. package/config/enable_peek.mjs.map +1 -0
  25. package/helpers/fetch.d.ts +4 -3
  26. package/helpers/fetch.js.map +1 -1
  27. package/helpers/fetch.mjs.map +1 -1
  28. package/helpers/pageHash.js.map +1 -1
  29. package/helpers/pageHash.mjs.map +1 -1
  30. package/helpers/pageHashParams.js.map +1 -1
  31. package/helpers/pageHashParams.mjs.map +1 -1
  32. package/helpers/time.d.ts +2 -2
  33. package/helpers/time.js.map +1 -1
  34. package/helpers/time.mjs.map +1 -1
  35. package/history.js +2 -2
  36. package/history.js.map +1 -1
  37. package/history.mjs +3 -3
  38. package/history.mjs.map +1 -1
  39. package/index.d.ts +30 -9
  40. package/index.js +877 -661
  41. package/index.js.map +1 -1
  42. package/index.mjs +874 -658
  43. package/index.mjs.map +1 -1
  44. package/package.json +22 -25
  45. package/persist-plugins/async-storage.d.ts +3 -3
  46. package/persist-plugins/async-storage.js +8 -7
  47. package/persist-plugins/async-storage.js.map +1 -1
  48. package/persist-plugins/async-storage.mjs +9 -8
  49. package/persist-plugins/async-storage.mjs.map +1 -1
  50. package/persist-plugins/fetch.js.map +1 -1
  51. package/persist-plugins/fetch.mjs.map +1 -1
  52. package/persist-plugins/firebase.d.ts +1 -1
  53. package/persist-plugins/firebase.js +12 -11
  54. package/persist-plugins/firebase.js.map +1 -1
  55. package/persist-plugins/firebase.mjs +13 -12
  56. package/persist-plugins/firebase.mjs.map +1 -1
  57. package/persist-plugins/indexeddb.d.ts +10 -10
  58. package/persist-plugins/indexeddb.js +2 -2
  59. package/persist-plugins/indexeddb.js.map +1 -1
  60. package/persist-plugins/indexeddb.mjs +2 -2
  61. package/persist-plugins/indexeddb.mjs.map +1 -1
  62. package/persist-plugins/local-storage.d.ts +3 -4
  63. package/persist-plugins/local-storage.js +19 -7
  64. package/persist-plugins/local-storage.js.map +1 -1
  65. package/persist-plugins/local-storage.mjs +20 -9
  66. package/persist-plugins/local-storage.mjs.map +1 -1
  67. package/persist-plugins/mmkv.d.ts +8 -8
  68. package/persist-plugins/mmkv.js +5 -4
  69. package/persist-plugins/mmkv.js.map +1 -1
  70. package/persist-plugins/mmkv.mjs +6 -5
  71. package/persist-plugins/mmkv.mjs.map +1 -1
  72. package/persist-plugins/query.js.map +1 -1
  73. package/persist-plugins/query.mjs.map +1 -1
  74. package/persist.d.ts +2 -14
  75. package/persist.js +1250 -268
  76. package/persist.js.map +1 -1
  77. package/persist.mjs +1250 -269
  78. package/persist.mjs.map +1 -1
  79. package/react-hooks/createObservableHook.js +1 -1
  80. package/react-hooks/createObservableHook.js.map +1 -1
  81. package/react-hooks/createObservableHook.mjs +1 -1
  82. package/react-hooks/createObservableHook.mjs.map +1 -1
  83. package/react-hooks/useFetch.d.ts +4 -3
  84. package/react-hooks/useFetch.js.map +1 -1
  85. package/react-hooks/useFetch.mjs.map +1 -1
  86. package/react-hooks/useHover.js.map +1 -1
  87. package/react-hooks/useHover.mjs.map +1 -1
  88. package/react-hooks/useMeasure.js.map +1 -1
  89. package/react-hooks/useMeasure.mjs.map +1 -1
  90. package/react-hooks/useObservableNextRouter.js.map +1 -1
  91. package/react-hooks/useObservableNextRouter.mjs.map +1 -1
  92. package/react-hooks/useObservableQuery.js.map +1 -1
  93. package/react-hooks/useObservableQuery.mjs.map +1 -1
  94. package/react-hooks/usePersistedObservable.d.ts +5 -3
  95. package/react-hooks/usePersistedObservable.js +5 -2
  96. package/react-hooks/usePersistedObservable.js.map +1 -1
  97. package/react-hooks/usePersistedObservable.mjs +5 -2
  98. package/react-hooks/usePersistedObservable.mjs.map +1 -1
  99. package/react.js +61 -75
  100. package/react.js.map +1 -1
  101. package/react.mjs +61 -75
  102. package/react.mjs.map +1 -1
  103. package/src/ObservableObject.ts +1184 -0
  104. package/src/ObservablePrimitive.ts +62 -0
  105. package/src/babel/index.ts +70 -0
  106. package/src/batching.ts +372 -0
  107. package/src/computed.ts +16 -0
  108. package/src/config/enable$get.ts +30 -0
  109. package/src/config/enableReactComponents.ts +26 -0
  110. package/src/config/enableReactNativeComponents.ts +102 -0
  111. package/src/config/enableReactTracking.ts +60 -0
  112. package/src/config/enableReactUse.ts +23 -0
  113. package/src/config/enable_peek.ts +31 -0
  114. package/src/config.ts +47 -0
  115. package/src/createObservable.ts +46 -0
  116. package/src/event.ts +26 -0
  117. package/src/globals.ts +224 -0
  118. package/src/helpers/fetch.ts +26 -0
  119. package/src/helpers/pageHash.ts +41 -0
  120. package/src/helpers/pageHashParams.ts +55 -0
  121. package/src/helpers/time.ts +30 -0
  122. package/src/helpers.ts +221 -0
  123. package/src/history/trackHistory.ts +29 -0
  124. package/src/is.ts +56 -0
  125. package/src/linked.ts +6 -0
  126. package/src/observable.ts +32 -0
  127. package/src/observableInterfaces.ts +165 -0
  128. package/src/observableTypes.ts +221 -0
  129. package/src/observe.ts +89 -0
  130. package/src/onChange.ts +136 -0
  131. package/src/persist/configureObservablePersistence.ts +7 -0
  132. package/src/persist/fieldTransformer.ts +149 -0
  133. package/src/persist/observablePersistRemoteFunctionsAdapter.ts +39 -0
  134. package/src/persist/persistObservable.ts +1029 -0
  135. package/src/persist-plugins/async-storage.ts +102 -0
  136. package/src/persist-plugins/fetch.ts +33 -0
  137. package/src/persist-plugins/firebase.ts +1050 -0
  138. package/src/persist-plugins/indexeddb.ts +433 -0
  139. package/src/persist-plugins/local-storage.ts +90 -0
  140. package/src/persist-plugins/mmkv.ts +90 -0
  141. package/src/persist-plugins/query.ts +133 -0
  142. package/src/persistTypes.ts +226 -0
  143. package/src/proxy.ts +28 -0
  144. package/src/react/Computed.tsx +7 -0
  145. package/src/react/For.tsx +116 -0
  146. package/src/react/Memo.tsx +4 -0
  147. package/src/react/Reactive.tsx +53 -0
  148. package/src/react/Show.tsx +33 -0
  149. package/src/react/Switch.tsx +43 -0
  150. package/src/react/react-globals.ts +3 -0
  151. package/src/react/{reactInterfaces.d.ts → reactInterfaces.ts} +15 -7
  152. package/src/react/reactive-observer.tsx +210 -0
  153. package/src/react/useComputed.ts +36 -0
  154. package/src/react/useEffectOnce.ts +41 -0
  155. package/src/react/useIsMounted.ts +16 -0
  156. package/src/react/useMount.ts +15 -0
  157. package/src/react/useObservable.ts +24 -0
  158. package/src/react/useObservableReducer.ts +52 -0
  159. package/src/react/useObservableState.ts +30 -0
  160. package/src/react/useObserve.ts +54 -0
  161. package/src/react/useObserveEffect.ts +40 -0
  162. package/src/react/usePauseProvider.tsx +13 -0
  163. package/src/react/useSelector.ts +167 -0
  164. package/src/react/useUnmount.ts +8 -0
  165. package/src/react/useWhen.ts +9 -0
  166. package/src/react-hooks/createObservableHook.ts +53 -0
  167. package/src/react-hooks/useFetch.ts +16 -0
  168. package/src/react-hooks/useHover.ts +40 -0
  169. package/src/react-hooks/useMeasure.ts +48 -0
  170. package/src/react-hooks/useObservableNextRouter.ts +137 -0
  171. package/src/react-hooks/useObservableQuery.ts +205 -0
  172. package/src/react-hooks/usePersistedObservable.ts +24 -0
  173. package/src/retry.ts +69 -0
  174. package/src/setupTracking.ts +26 -0
  175. package/src/sync/activateSyncedNode.ts +146 -0
  176. package/src/sync/configureObservableSync.ts +7 -0
  177. package/src/sync/syncHelpers.ts +15 -0
  178. package/src/sync/syncObservable.ts +989 -0
  179. package/src/sync/syncObservableAdapter.ts +30 -0
  180. package/src/sync/synced.ts +20 -0
  181. package/src/sync-plugins/fetch.ts +42 -0
  182. package/src/syncTypes.ts +163 -0
  183. package/src/trace/traceHelpers.ts +11 -0
  184. package/src/trace/useTraceListeners.ts +34 -0
  185. package/src/trace/useTraceUpdates.ts +24 -0
  186. package/src/trace/useVerifyNotTracking.ts +33 -0
  187. package/src/trace/useVerifyOneRender.ts +10 -0
  188. package/src/trackSelector.ts +52 -0
  189. package/src/tracking.ts +43 -0
  190. package/src/types/babel.d.ts +12 -0
  191. package/src/when.ts +70 -0
  192. package/sync-plugins/fetch.d.ts +11 -0
  193. package/sync-plugins/fetch.js +24 -0
  194. package/sync-plugins/fetch.js.map +1 -0
  195. package/sync-plugins/fetch.mjs +22 -0
  196. package/sync-plugins/fetch.mjs.map +1 -0
  197. package/sync.d.ts +8 -0
  198. package/sync.js +919 -0
  199. package/sync.js.map +1 -0
  200. package/sync.mjs +912 -0
  201. package/sync.mjs.map +1 -0
  202. package/trace.js +13 -10
  203. package/trace.js.map +1 -1
  204. package/trace.mjs +11 -8
  205. package/trace.mjs.map +1 -1
  206. package/types/babel.d.ts +3 -3
  207. package/config/enableDirectAccess.d.ts +0 -7
  208. package/config/enableDirectAccess.js +0 -25
  209. package/config/enableDirectAccess.js.map +0 -1
  210. package/config/enableDirectAccess.mjs +0 -23
  211. package/config/enableDirectAccess.mjs.map +0 -1
  212. package/config/enableDirectPeek.d.ts +0 -7
  213. package/config/enableDirectPeek.js.map +0 -1
  214. package/config/enableDirectPeek.mjs.map +0 -1
  215. package/config/enableReactDirectRender.d.ts +0 -2
  216. package/config/enableReactDirectRender.js +0 -78
  217. package/config/enableReactDirectRender.js.map +0 -1
  218. package/config/enableReactDirectRender.mjs +0 -75
  219. package/config/enableReactDirectRender.mjs.map +0 -1
  220. package/src/ObservableObject.d.ts +0 -14
  221. package/src/ObservablePrimitive.d.ts +0 -7
  222. package/src/babel/index.d.ts +0 -17
  223. package/src/batching.d.ts +0 -6
  224. package/src/computed.d.ts +0 -4
  225. package/src/config/enableDirectAccess.d.ts +0 -7
  226. package/src/config/enableDirectPeek.d.ts +0 -7
  227. package/src/config/enableReactComponents.d.ts +0 -7
  228. package/src/config/enableReactDirectRender.d.ts +0 -2
  229. package/src/config/enableReactNativeComponents.d.ts +0 -20
  230. package/src/config/enableReactTracking.d.ts +0 -15
  231. package/src/config/enableReactUse.d.ts +0 -7
  232. package/src/config.d.ts +0 -8
  233. package/src/createObservable.d.ts +0 -2
  234. package/src/event.d.ts +0 -2
  235. package/src/globals.d.ts +0 -32
  236. package/src/helpers/fetch.d.ts +0 -6
  237. package/src/helpers/pageHash.d.ts +0 -7
  238. package/src/helpers/pageHashParams.d.ts +0 -7
  239. package/src/helpers/time.d.ts +0 -3
  240. package/src/helpers.d.ts +0 -13
  241. package/src/history/trackHistory.d.ts +0 -4
  242. package/src/is.d.ts +0 -10
  243. package/src/observable.d.ts +0 -16
  244. package/src/observableInterfaces.d.ts +0 -456
  245. package/src/observe.d.ts +0 -6
  246. package/src/onChange.d.ts +0 -7
  247. package/src/persist/configureObservablePersistence.d.ts +0 -3
  248. package/src/persist/fieldTransformer.d.ts +0 -8
  249. package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +0 -2
  250. package/src/persist/persistActivateNode.d.ts +0 -1
  251. package/src/persist/persistHelpers.d.ts +0 -1
  252. package/src/persist/persistObservable.d.ts +0 -25
  253. package/src/persist-plugins/async-storage.d.ts +0 -14
  254. package/src/persist-plugins/fetch.d.ts +0 -10
  255. package/src/persist-plugins/firebase.d.ts +0 -51
  256. package/src/persist-plugins/indexeddb.d.ts +0 -25
  257. package/src/persist-plugins/local-storage.d.ts +0 -21
  258. package/src/persist-plugins/mmkv.d.ts +0 -14
  259. package/src/persist-plugins/query.d.ts +0 -18
  260. package/src/proxy.d.ts +0 -5
  261. package/src/react/Computed.d.ts +0 -5
  262. package/src/react/For.d.ts +0 -15
  263. package/src/react/Memo.d.ts +0 -3
  264. package/src/react/Reactive.d.ts +0 -9
  265. package/src/react/Show.d.ts +0 -18
  266. package/src/react/Switch.d.ts +0 -14
  267. package/src/react/react-globals.d.ts +0 -3
  268. package/src/react/reactive-observer.d.ts +0 -14
  269. package/src/react/useComputed.d.ts +0 -5
  270. package/src/react/useEffectOnce.d.ts +0 -1
  271. package/src/react/useIsMounted.d.ts +0 -2
  272. package/src/react/useMount.d.ts +0 -2
  273. package/src/react/useObservable.d.ts +0 -9
  274. package/src/react/useObservableReducer.d.ts +0 -7
  275. package/src/react/useObservableState.d.ts +0 -2
  276. package/src/react/useObserve.d.ts +0 -4
  277. package/src/react/useObserveEffect.d.ts +0 -4
  278. package/src/react/usePauseProvider.d.ts +0 -8
  279. package/src/react/useSelector.d.ts +0 -3
  280. package/src/react/useUnmount.d.ts +0 -2
  281. package/src/react/useWhen.d.ts +0 -3
  282. package/src/react-hooks/createObservableHook.d.ts +0 -2
  283. package/src/react-hooks/useFetch.d.ts +0 -6
  284. package/src/react-hooks/useHover.d.ts +0 -3
  285. package/src/react-hooks/useMeasure.d.ts +0 -6
  286. package/src/react-hooks/useObservableNextRouter.d.ts +0 -33
  287. package/src/react-hooks/useObservableQuery.d.ts +0 -6
  288. package/src/react-hooks/usePersistedObservable.d.ts +0 -11
  289. package/src/retry.d.ts +0 -9
  290. package/src/setupTracking.d.ts +0 -2
  291. package/src/trace/traceHelpers.d.ts +0 -2
  292. package/src/trace/useTraceListeners.d.ts +0 -1
  293. package/src/trace/useTraceUpdates.d.ts +0 -1
  294. package/src/trace/useVerifyNotTracking.d.ts +0 -1
  295. package/src/trace/useVerifyOneRender.d.ts +0 -1
  296. package/src/trackSelector.d.ts +0 -7
  297. package/src/tracking.d.ts +0 -13
  298. package/src/when.d.ts +0 -3
@@ -1,23 +1,31 @@
1
1
  import type { FC, LegacyRef, ReactNode } from 'react';
2
- import type { Observable, Selector } from '../observableInterfaces';
2
+ import type { Observable } from '../observableTypes';
3
+ import type { Selector } from '../observableInterfaces';
4
+
3
5
  export type ShapeWithNew$<T> = Partial<Omit<T, 'children'>> & {
4
6
  [K in keyof T as K extends `$${string & K}` ? K : `$${string & K}`]?: Selector<T[K]>;
5
- } & {
6
- children?: Selector<ReactNode>;
7
- };
7
+ } & { children?: Selector<ReactNode> };
8
+
8
9
  export interface BindKey<P> {
9
10
  handler?: keyof P;
10
11
  getValue?: (e: any) => any;
11
12
  defaultValue?: any;
12
13
  selector?: (propsOut: Record<string, any>, p: Observable<any>) => any;
13
14
  }
15
+
14
16
  export type BindKeys<P = any> = Record<keyof P, BindKey<P>>;
17
+
15
18
  export type FCReactiveObject<T> = {
16
19
  [K in keyof T]: FC<ShapeWithNew$<T[K]>>;
17
20
  };
18
- export type FCReactive<P, P2> = P & FC<ShapeWithNew$<P2> & {
19
- ref?: LegacyRef<P> | undefined;
20
- }>;
21
+
22
+ export type FCReactive<P, P2> = P &
23
+ FC<
24
+ ShapeWithNew$<P2> & {
25
+ ref?: LegacyRef<P> | undefined;
26
+ }
27
+ >;
28
+
21
29
  export interface UseSelectorOptions {
22
30
  suspense?: boolean;
23
31
  skipCheck?: boolean;
@@ -0,0 +1,210 @@
1
+ import { isFunction, isObservable, Selector } from '@legendapp/state';
2
+ import { ChangeEvent, FC, forwardRef, memo, useCallback } from 'react';
3
+ import { reactGlobals } from './react-globals';
4
+ import type { BindKeys } from './reactInterfaces';
5
+ import { useSelector } from './useSelector';
6
+
7
+ export type ShapeWithPick$<T, T2 extends keyof T = keyof T> = Partial<T> & {
8
+ [K in T2 as K extends `$${string & K}` ? K : `$${string & K}`]?: Selector<T[K]>;
9
+ };
10
+ export type ShapeWith$<T> = Partial<T> & {
11
+ [K in keyof T as K extends `$${string & K}` ? K : `$${string & K}`]?: Selector<T[K]>;
12
+ };
13
+ export type ObjectShapeWith$<T> = {
14
+ [K in keyof T]: T[K] extends FC<infer P> ? FC<ShapeWith$<P>> : T[K];
15
+ };
16
+ export type ExtractFCPropsType<T> = T extends FC<infer P> ? P : never;
17
+
18
+ // Extracting the forwardRef inspired by https://github.com/mobxjs/mobx/blob/main/packages/mobx-react-lite/src/observer.ts
19
+ export const hasSymbol = /* @__PURE__ */ typeof Symbol === 'function' && Symbol.for;
20
+
21
+ // TODOV2: Change bindKeys to an options object, where one of the options is "convertChildren" so that behavior can be optional
22
+ function createReactiveComponent<P = object>(
23
+ component: FC<P>,
24
+ observe: boolean,
25
+ reactive?: boolean,
26
+ bindKeys?: BindKeys<P>,
27
+ ) {
28
+ const ReactForwardRefSymbol = hasSymbol
29
+ ? Symbol.for('react.forward_ref')
30
+ : // eslint-disable-next-line react/display-name, @typescript-eslint/no-unused-vars
31
+ typeof forwardRef === 'function' && forwardRef((props: any) => null)['$$typeof'];
32
+
33
+ const ReactMemoSymbol = hasSymbol
34
+ ? Symbol.for('react.memo')
35
+ : // eslint-disable-next-line react/display-name, @typescript-eslint/no-unused-vars
36
+ typeof forwardRef === 'function' && memo((props) => null)['$$typeof'];
37
+
38
+ // If this component is already reactive bail out early
39
+ // This can happen with Fast Refresh.
40
+ if ((component as any)['__legend_proxied']) return component;
41
+
42
+ let useForwardRef = false;
43
+ let useMemo = false;
44
+ let render = component;
45
+
46
+ // Unwrap memo on the component
47
+ if (ReactMemoSymbol && (render as any)['$$typeof'] === ReactMemoSymbol && (render as any)['type']) {
48
+ useMemo = true;
49
+ render = (render as any)['type'];
50
+ }
51
+ // Unwrap forwardRef on the component
52
+ if (ReactForwardRefSymbol && (render as any)['$$typeof'] === ReactForwardRefSymbol) {
53
+ useForwardRef = true;
54
+ render = (render as any)['render'];
55
+ if (process.env.NODE_ENV === 'development' && typeof render !== 'function') {
56
+ throw new Error(`[legend-state] \`render\` property of ForwardRef was not a function`);
57
+ }
58
+ }
59
+
60
+ const proxyHandler: ProxyHandler<any> = {
61
+ apply(target, thisArg, argArray) {
62
+ // If this is a reactive component, convert all props ending in $
63
+ // to regular props and set up a useSelector listener
64
+ if (reactive) {
65
+ const props = argArray[0];
66
+ const propsOut = {} as Record<string, any>;
67
+ const keys = Object.keys(props);
68
+ for (let i = 0; i < keys.length; i++) {
69
+ const key = keys[i];
70
+ const p = props[key];
71
+
72
+ // Convert children if it's a function
73
+ if (key === 'children' && (isFunction(p) || isObservable(p))) {
74
+ props[key] = useSelector(p, { skipCheck: true });
75
+ }
76
+ // Convert reactive props
77
+ else if (key.startsWith('$') || key.endsWith('$')) {
78
+ // TODOV3 Add this warning and then remove the deprecated endsWith option
79
+ // if (
80
+ // process.env.NODE_ENV === 'development' &&
81
+ // !internal.globalState.noDepWarn &&
82
+ // key.endsWith('$')
83
+ // ) {
84
+ // console.warn(
85
+ // `[legend-state] Reactive props will be changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
86
+ // '$',
87
+ // '',
88
+ // )}. See https://legendapp.com/open-source/state/migrating for more details.`,
89
+ // );
90
+ // }
91
+ const k = key.endsWith('$') ? key.slice(0, -1) : key.slice(1);
92
+ // Return raw value and listen to the selector for changes
93
+
94
+ const bind = bindKeys?.[k as keyof P];
95
+ const shouldBind = bind && isObservable(p);
96
+
97
+ propsOut[k] = shouldBind && bind?.selector ? bind.selector(propsOut, p) : useSelector(p);
98
+
99
+ // If this key is one of the bind keys set up a two-way binding
100
+ if (shouldBind) {
101
+ // Use the bind's defaultValue if value is undefined
102
+ if (bind.defaultValue !== undefined && propsOut[k] === undefined) {
103
+ propsOut[k] = bind.defaultValue;
104
+ }
105
+
106
+ if (bind.handler && bind.getValue) {
107
+ // Hook up the change lander
108
+ const handlerFn = (e: ChangeEvent) => {
109
+ p.set(bind.getValue!(e));
110
+ props[bind.handler!]?.(e);
111
+ };
112
+
113
+ (propsOut[bind.handler as string] as any) =
114
+ // If in development mode, don't memoize the handler. fix fast refresh bug
115
+ process.env.NODE_ENV === 'development'
116
+ ? handlerFn
117
+ : useCallback(handlerFn, [props[bind.handler], bindKeys]);
118
+ }
119
+ }
120
+
121
+ // Delete the reactive key
122
+ delete propsOut[key];
123
+ } else if (propsOut[key] === undefined) {
124
+ propsOut[key] = p;
125
+ }
126
+ }
127
+ argArray[0] = propsOut;
128
+ }
129
+
130
+ // If observing wrap the whole render in a useSelector to listen to it
131
+ if (observe) {
132
+ return useSelector(
133
+ () => {
134
+ reactGlobals.inObserver = true;
135
+ try {
136
+ return Reflect.apply(target, thisArg, argArray);
137
+ } finally {
138
+ reactGlobals.inObserver = false;
139
+ }
140
+ },
141
+ { skipCheck: true },
142
+ );
143
+ } else {
144
+ return Reflect.apply(target, thisArg, argArray);
145
+ }
146
+ },
147
+ };
148
+
149
+ const proxy = new Proxy(render, proxyHandler);
150
+
151
+ let ret;
152
+
153
+ if (useForwardRef) {
154
+ ret = forwardRef(proxy);
155
+ (ret as any)['__legend_proxied'] = true;
156
+ } else {
157
+ ret = proxy;
158
+ }
159
+
160
+ return observe || useMemo ? memo(ret) : ret;
161
+ }
162
+
163
+ export function observer<P extends FC<any>>(component: P): P {
164
+ return createReactiveComponent(component, true);
165
+ }
166
+
167
+ export function reactive<T extends FC<any>>(
168
+ component: T,
169
+ bindKeys?: BindKeys<ExtractFCPropsType<T>>,
170
+ ): T | FC<ShapeWith$<ExtractFCPropsType<T>>>;
171
+ export function reactive<T extends FC<any>, T2 extends keyof ExtractFCPropsType<T>>(
172
+ component: T,
173
+ bindKeys?: BindKeys<ExtractFCPropsType<T>>,
174
+ ): T | FC<ShapeWithPick$<ExtractFCPropsType<T>, T2>>;
175
+ export function reactive<T extends FC<any>>(
176
+ component: T,
177
+ bindKeys?: BindKeys<ExtractFCPropsType<T>>,
178
+ ): T | FC<ShapeWith$<ExtractFCPropsType<T>>> {
179
+ return createReactiveComponent(component, false, true, bindKeys);
180
+ }
181
+
182
+ export function reactiveObserver<T extends FC<any>>(
183
+ component: T,
184
+ bindKeys?: BindKeys<ExtractFCPropsType<T>>,
185
+ ): T | FC<ShapeWith$<ExtractFCPropsType<T>>>;
186
+ export function reactiveObserver<T extends FC<any>, T2 extends keyof ExtractFCPropsType<T>>(
187
+ component: T,
188
+ bindKeys?: BindKeys<ExtractFCPropsType<T>>,
189
+ ): T | FC<ShapeWithPick$<ExtractFCPropsType<T>, T2>>;
190
+ export function reactiveObserver<T extends FC<any>>(
191
+ component: T,
192
+ bindKeys?: BindKeys<ExtractFCPropsType<T>>,
193
+ ): T | FC<ShapeWith$<ExtractFCPropsType<T>>> {
194
+ return createReactiveComponent(component, true, true, bindKeys);
195
+ }
196
+
197
+ export function reactiveComponents<P extends Record<string, FC>>(components: P): ObjectShapeWith$<P> {
198
+ return new Proxy(
199
+ {},
200
+ {
201
+ get(target: Record<string, any>, p: string) {
202
+ if (!target[p] && components[p]) {
203
+ target[p] = createReactiveComponent(components[p], false, true) as FC<ShapeWith$<P>>;
204
+ }
205
+
206
+ return target[p];
207
+ },
208
+ },
209
+ ) as ObjectShapeWith$<P>;
210
+ }
@@ -0,0 +1,36 @@
1
+ import { computed, isArray, isFunction, Observable, ObservableParam } from '@legendapp/state';
2
+ import { useMemo, useRef } from 'react';
3
+
4
+ export function useComputed<T>(compute: () => T | Promise<T>): Observable<T>;
5
+ export function useComputed<T>(compute: () => T | Promise<T>, deps: any[]): Observable<T>;
6
+ export function useComputed<T, T2 = T>(
7
+ compute: (() => T | Promise<T>) | ObservableParam<T>,
8
+ set: (value: T2) => void,
9
+ ): Observable<T>;
10
+ export function useComputed<T, T2 = T>(
11
+ compute: (() => T | Promise<T>) | ObservableParam<T>,
12
+ set: (value: T2) => void,
13
+ deps: any[],
14
+ ): Observable<T>;
15
+ export function useComputed<T, T2 = T>(
16
+ compute: (() => T | Promise<T>) | ObservableParam<T>,
17
+ set?: ((value: T2) => void) | any[],
18
+ deps?: any[],
19
+ ): Observable<T> {
20
+ if (!deps && isArray(set)) {
21
+ deps = set;
22
+ set = undefined;
23
+ }
24
+ const ref = useRef<{ compute?: (() => T | Promise<T>) | ObservableParam<T>; set?: (value: T2) => void }>({});
25
+ ref.current.compute = compute;
26
+ ref.current.set = set as (value: T2) => void;
27
+
28
+ return useMemo(
29
+ () =>
30
+ computed<T, T2>(
31
+ () => (isFunction(ref.current.compute) ? ref.current.compute() : ref.current.compute) as any,
32
+ (set ? (value) => ref.current.set!(value) : undefined) as (value: T2) => void,
33
+ ),
34
+ deps || [],
35
+ );
36
+ }
@@ -0,0 +1,41 @@
1
+ import { isFunction } from '@legendapp/state';
2
+ import { useEffect, useRef } from 'react';
3
+
4
+ export const useEffectOnce = (effect: () => void | (() => void), deps: any[]) => {
5
+ if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') {
6
+ const refDispose = useRef<{ dispose?: void | (() => void); num: number }>({ num: 0 });
7
+
8
+ useEffect(() => {
9
+ // This is a hack to work around StrictMode running effects twice.
10
+ // On the first run it returns a cleanup function that queues the dispose function
11
+ // in a microtask. This way it will run at the end of the frame after StrictMode's second
12
+ // run of the effect. If it's run a second time then the microtasked dispose will do nothing,
13
+ // but the effect will return the dispose again so that when it actually unmounts it will dispose.
14
+ // If not in StrictMode, then the dispose function will run in the microtask.
15
+ // It's possible that this is not safe in 100% of cases, but I'm not sure what the
16
+ // dangerous cases would be. The side effect is that the listener is still active
17
+ // until the end of the frame, but that's probably not a problem.
18
+ const { current } = refDispose;
19
+ current.num++;
20
+ const dispose = () => {
21
+ if (current.dispose && current.num < 2) {
22
+ (current.dispose as () => void)();
23
+ current.dispose = undefined;
24
+ }
25
+ current.num--;
26
+ };
27
+ if (current.dispose === undefined) {
28
+ const ret = effect() ?? null;
29
+ // If ret is a function, then it's a dispose function.
30
+ if (ret && isFunction(ret)) {
31
+ current.dispose = ret;
32
+ return () => queueMicrotask(dispose);
33
+ }
34
+ } else {
35
+ return dispose;
36
+ }
37
+ }, deps);
38
+ } else {
39
+ useEffect(effect, deps);
40
+ }
41
+ };
@@ -0,0 +1,16 @@
1
+ import type { Observable } from '@legendapp/state';
2
+ import { useMountOnce } from './useMount';
3
+ import { useObservable } from './useObservable';
4
+
5
+ export function useIsMounted(): Observable<boolean> {
6
+ const obs = useObservable(false);
7
+
8
+ const { set } = obs;
9
+ useMountOnce(() => {
10
+ set(true);
11
+
12
+ return () => set(false);
13
+ });
14
+
15
+ return obs;
16
+ }
@@ -0,0 +1,15 @@
1
+ import { isPromise } from '@legendapp/state';
2
+ import { useEffectOnce } from './useEffectOnce';
3
+
4
+ export function useMount(fn: () => (void | (() => void)) | Promise<void>) {
5
+ return useEffectOnce(() => {
6
+ const ret = fn();
7
+ // Allow the function to be async but if so ignore its return value
8
+ if (!isPromise(ret)) {
9
+ return ret;
10
+ }
11
+ }, []);
12
+ }
13
+
14
+ // TODOV4 Deprecate
15
+ export const useMountOnce = useMount;
@@ -0,0 +1,24 @@
1
+ import { observable, Observable, RecursiveValueOrFunction } from '@legendapp/state';
2
+ import { useRef } from 'react';
3
+
4
+ /**
5
+ * A React hook that creates a new observable
6
+ *
7
+ * @param initialValue The initial value of the observable or a function that returns the initial value
8
+ *
9
+ * @see https://www.legendapp.com/dev/state/react/#useObservable
10
+ */
11
+ export function useObservable<T>(): Observable<T | undefined>;
12
+ export function useObservable<T>(
13
+ value: Promise<RecursiveValueOrFunction<T>> | (() => RecursiveValueOrFunction<T>) | RecursiveValueOrFunction<T>,
14
+ ): Observable<T>;
15
+ export function useObservable<T>(value: T): Observable<T>;
16
+ export function useObservable<T>(value?: T): Observable<any>;
17
+ export function useObservable<T>(initialValue?: T | (() => T) | (() => Promise<T>)): Observable<T> {
18
+ const ref = useRef<Observable<T>>();
19
+ if (!ref.current) {
20
+ // Create the observable from the default value
21
+ ref.current = observable<T>(initialValue as any);
22
+ }
23
+ return ref.current;
24
+ }
@@ -0,0 +1,52 @@
1
+ import type { Observable } from '@legendapp/state';
2
+ import { isFunction } from '@legendapp/state';
3
+ import type {
4
+ Dispatch,
5
+ DispatchWithoutAction,
6
+ Reducer,
7
+ ReducerAction,
8
+ ReducerState,
9
+ ReducerStateWithoutAction,
10
+ ReducerWithoutAction,
11
+ } from 'react';
12
+ import { useObservable } from './useObservable';
13
+
14
+ export function useObservableReducer<R extends ReducerWithoutAction<any>, I>(
15
+ reducer: R,
16
+ initializerArg: I,
17
+ initializer: (arg: I) => ReducerStateWithoutAction<R>,
18
+ ): [Observable<ReducerStateWithoutAction<R>>, DispatchWithoutAction];
19
+ export function useObservableReducer<R extends ReducerWithoutAction<any>>(
20
+ reducer: R,
21
+ initializerArg: ReducerStateWithoutAction<R>,
22
+ initializer?: undefined,
23
+ ): [Observable<ReducerStateWithoutAction<R>>, DispatchWithoutAction];
24
+ export function useObservableReducer<R extends Reducer<any, any>, I>(
25
+ reducer: R,
26
+ initializerArg: I & ReducerState<R>,
27
+ initializer: (arg: I & ReducerState<R>) => ReducerState<R>,
28
+ ): [Observable<ReducerState<R>>, Dispatch<ReducerAction<R>>];
29
+ export function useObservableReducer<R extends Reducer<any, any>, I>(
30
+ reducer: R,
31
+ initializerArg: I,
32
+ initializer: (arg: I) => ReducerState<R>,
33
+ ): [Observable<ReducerState<R>>, Dispatch<ReducerAction<R>>];
34
+ export function useObservableReducer<R extends Reducer<any, any>>(
35
+ reducer: R,
36
+ initialState: ReducerState<R>,
37
+ initializer?: undefined,
38
+ ): [Observable<ReducerState<R>>, Dispatch<ReducerAction<R>>];
39
+ export function useObservableReducer<R extends Reducer<any, any>, I>(
40
+ reducer: R,
41
+ initializerArg: I & ReducerState<R>,
42
+ initializer: ((arg: I & ReducerState<R>) => ReducerState<R>) | undefined,
43
+ ): [Observable<ReducerState<R>>, Dispatch<ReducerAction<R>>] {
44
+ const obs = useObservable(() =>
45
+ initializerArg !== undefined && isFunction(initializerArg) ? initializer!(initializerArg) : initializerArg,
46
+ );
47
+ const dispatch = (action: any) => {
48
+ obs.set(reducer(obs.get(), action));
49
+ };
50
+
51
+ return [obs as Observable<ReducerState<R>>, dispatch];
52
+ }
@@ -0,0 +1,30 @@
1
+ import { isFunction, observable, type Observable } from '@legendapp/state';
2
+ import { useMemo } from 'react';
3
+ import { useSelector } from './useSelector';
4
+
5
+ export function useObservableState<T>(initialValue?: T | (() => T) | (() => Promise<T>)): [Observable<T>, T] {
6
+ // Create a memoized observable
7
+ return useMemo(
8
+ () =>
9
+ // Wrap the return array in a Proxy to detect whether the value is accessed
10
+ new Proxy(
11
+ [
12
+ observable<T>((isFunction(initialValue) ? initialValue() : initialValue) as any),
13
+ // Second element of the array just needs to exist for the Proxy to access it
14
+ // but we can't ensure it's updated with the real value, and it doesn't really matter since it's proxied,
15
+ // so just make it undefined. Alternatively the Proxy handler could manually return 2 for the "length" prop
16
+ // but this seems easier and less code.
17
+ undefined,
18
+ ],
19
+ proxyHandler,
20
+ ),
21
+ [],
22
+ ) as [Observable<T>, T];
23
+ }
24
+
25
+ const proxyHandler: ProxyHandler<any[]> = {
26
+ get(target, prop, receiver) {
27
+ // If the value is accessed at index 1 then `useSelector` to track it for changes
28
+ return prop === '1' ? useSelector(target[0]) : Reflect.get(target, prop, receiver);
29
+ },
30
+ };
@@ -0,0 +1,54 @@
1
+ import {
2
+ computeSelector,
3
+ isFunction,
4
+ ObservableParam,
5
+ observe,
6
+ ObserveEvent,
7
+ ObserveEventCallback,
8
+ Selector,
9
+ } from '@legendapp/state';
10
+ import { useRef } from 'react';
11
+ import type { ObserveOptions } from '../observe';
12
+ import { useUnmountOnce } from './useUnmount';
13
+
14
+ export function useObserve<T>(run: (e: ObserveEvent<T>) => T | void, options?: ObserveOptions): () => void;
15
+ export function useObserve<T>(
16
+ selector: Selector<T>,
17
+ reaction?: (e: ObserveEventCallback<T>) => any,
18
+ options?: ObserveOptions,
19
+ ): () => void;
20
+ export function useObserve<T>(
21
+ selector: Selector<T> | ((e: ObserveEvent<T>) => any),
22
+ reactionOrOptions?: ((e: ObserveEventCallback<T>) => any) | ObserveOptions,
23
+ options?: ObserveOptions,
24
+ ): () => void {
25
+ let reaction: ((e: ObserveEventCallback<T>) => any) | undefined;
26
+ if (isFunction(reactionOrOptions)) {
27
+ reaction = reactionOrOptions;
28
+ } else {
29
+ options = reactionOrOptions;
30
+ }
31
+
32
+ const ref = useRef<{
33
+ selector?: Selector<T> | ((e: ObserveEvent<T>) => T | void) | ObservableParam<T>;
34
+ reaction?: (e: ObserveEventCallback<T>) => any;
35
+ dispose?: () => void;
36
+ }>({});
37
+
38
+ ref.current.selector = selector;
39
+ ref.current.reaction = reaction;
40
+
41
+ if (!ref.current.dispose) {
42
+ ref.current.dispose = observe<T>(
43
+ ((e: ObserveEventCallback<T>) => computeSelector(ref.current.selector, e)) as any,
44
+ (e) => ref.current.reaction?.(e),
45
+ options,
46
+ );
47
+ }
48
+
49
+ useUnmountOnce(() => {
50
+ ref.current?.dispose?.();
51
+ });
52
+
53
+ return ref.current.dispose;
54
+ }
@@ -0,0 +1,40 @@
1
+ import { isFunction, ObservableParam, observe, ObserveEvent, ObserveEventCallback, Selector } from '@legendapp/state';
2
+ import { useRef } from 'react';
3
+ import type { ObserveOptions } from '../observe';
4
+ import { useMountOnce } from './useMount';
5
+
6
+ export function useObserveEffect<T>(run: (e: ObserveEvent<T>) => T | void, options?: ObserveOptions): void;
7
+ export function useObserveEffect<T>(
8
+ selector: Selector<T>,
9
+ reaction?: (e: ObserveEventCallback<T>) => any,
10
+ options?: ObserveOptions,
11
+ ): void;
12
+ export function useObserveEffect<T>(
13
+ selector: Selector<T> | ((e: ObserveEvent<T>) => any),
14
+ reactionOrOptions?: ((e: ObserveEventCallback<T>) => any) | ObserveOptions,
15
+ options?: ObserveOptions,
16
+ ): void {
17
+ let reaction: ((e: ObserveEventCallback<T>) => any) | undefined;
18
+ if (isFunction(reactionOrOptions)) {
19
+ reaction = reactionOrOptions;
20
+ } else {
21
+ options = reactionOrOptions;
22
+ }
23
+
24
+ const ref = useRef<{
25
+ selector: Selector<T> | ((e: ObserveEvent<T>) => T | void) | ObservableParam<T>;
26
+ reaction?: (e: ObserveEventCallback<T>) => any;
27
+ }>({ selector });
28
+ ref.current = { selector, reaction };
29
+
30
+ useMountOnce(() =>
31
+ observe<T>(
32
+ ((e: ObserveEventCallback<T>) => {
33
+ const { selector } = ref.current as { selector: (e: ObserveEvent<T>) => T | void };
34
+ return isFunction(selector) ? selector(e) : selector;
35
+ }) as any,
36
+ (e) => ref.current.reaction?.(e),
37
+ options,
38
+ ),
39
+ );
40
+ }
@@ -0,0 +1,13 @@
1
+ import { Observable, observable } from '@legendapp/state';
2
+ import { ReactNode, createContext, createElement, useState } from 'react';
3
+
4
+ export const PauseContext = createContext<Observable<boolean>>(null as any);
5
+
6
+ export function usePauseProvider() {
7
+ const [value] = useState(() => observable(false));
8
+ return {
9
+ PauseProvider: ({ children }: { children: ReactNode }) =>
10
+ createElement(PauseContext.Provider, { value }, children),
11
+ isPaused$: value,
12
+ };
13
+ }