@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.
- package/README.md +4 -2
- package/babel.js.map +1 -1
- package/config/enable$get.d.ts +8 -0
- package/config/enable$get.js +24 -0
- package/config/enable$get.js.map +1 -0
- package/config/enable$get.mjs +21 -0
- package/config/enable$get.mjs.map +1 -0
- package/config/enableReactComponents.js.map +1 -1
- package/config/enableReactComponents.mjs.map +1 -1
- package/config/enableReactNativeComponents.js.map +1 -1
- package/config/enableReactNativeComponents.mjs.map +1 -1
- package/config/enableReactTracking.d.ts +0 -9
- package/config/enableReactTracking.js.map +1 -1
- package/config/enableReactTracking.mjs.map +1 -1
- package/config/enableReactUse.d.ts +1 -1
- package/config/enableReactUse.js +1 -0
- package/config/enableReactUse.js.map +1 -1
- package/config/enableReactUse.mjs +1 -0
- package/config/enableReactUse.mjs.map +1 -1
- package/config/enable_peek.d.ts +8 -0
- package/config/{enableDirectPeek.js → enable_peek.js} +6 -3
- package/config/enable_peek.js.map +1 -0
- package/config/{enableDirectPeek.mjs → enable_peek.mjs} +5 -3
- package/config/enable_peek.mjs.map +1 -0
- package/helpers/fetch.d.ts +4 -3
- package/helpers/fetch.js.map +1 -1
- package/helpers/fetch.mjs.map +1 -1
- package/helpers/pageHash.js.map +1 -1
- package/helpers/pageHash.mjs.map +1 -1
- package/helpers/pageHashParams.js.map +1 -1
- package/helpers/pageHashParams.mjs.map +1 -1
- package/helpers/time.d.ts +2 -2
- package/helpers/time.js.map +1 -1
- package/helpers/time.mjs.map +1 -1
- package/history.js +2 -2
- package/history.js.map +1 -1
- package/history.mjs +3 -3
- package/history.mjs.map +1 -1
- package/index.d.ts +30 -9
- package/index.js +877 -661
- package/index.js.map +1 -1
- package/index.mjs +874 -658
- package/index.mjs.map +1 -1
- package/package.json +22 -25
- package/persist-plugins/async-storage.d.ts +3 -3
- package/persist-plugins/async-storage.js +8 -7
- package/persist-plugins/async-storage.js.map +1 -1
- package/persist-plugins/async-storage.mjs +9 -8
- package/persist-plugins/async-storage.mjs.map +1 -1
- package/persist-plugins/fetch.js.map +1 -1
- package/persist-plugins/fetch.mjs.map +1 -1
- package/persist-plugins/firebase.d.ts +1 -1
- package/persist-plugins/firebase.js +12 -11
- package/persist-plugins/firebase.js.map +1 -1
- package/persist-plugins/firebase.mjs +13 -12
- package/persist-plugins/firebase.mjs.map +1 -1
- package/persist-plugins/indexeddb.d.ts +10 -10
- package/persist-plugins/indexeddb.js +2 -2
- package/persist-plugins/indexeddb.js.map +1 -1
- package/persist-plugins/indexeddb.mjs +2 -2
- package/persist-plugins/indexeddb.mjs.map +1 -1
- package/persist-plugins/local-storage.d.ts +3 -4
- package/persist-plugins/local-storage.js +19 -7
- package/persist-plugins/local-storage.js.map +1 -1
- package/persist-plugins/local-storage.mjs +20 -9
- package/persist-plugins/local-storage.mjs.map +1 -1
- package/persist-plugins/mmkv.d.ts +8 -8
- package/persist-plugins/mmkv.js +5 -4
- package/persist-plugins/mmkv.js.map +1 -1
- package/persist-plugins/mmkv.mjs +6 -5
- package/persist-plugins/mmkv.mjs.map +1 -1
- package/persist-plugins/query.js.map +1 -1
- package/persist-plugins/query.mjs.map +1 -1
- package/persist.d.ts +2 -14
- package/persist.js +1250 -268
- package/persist.js.map +1 -1
- package/persist.mjs +1250 -269
- package/persist.mjs.map +1 -1
- package/react-hooks/createObservableHook.js +1 -1
- package/react-hooks/createObservableHook.js.map +1 -1
- package/react-hooks/createObservableHook.mjs +1 -1
- package/react-hooks/createObservableHook.mjs.map +1 -1
- package/react-hooks/useFetch.d.ts +4 -3
- package/react-hooks/useFetch.js.map +1 -1
- package/react-hooks/useFetch.mjs.map +1 -1
- package/react-hooks/useHover.js.map +1 -1
- package/react-hooks/useHover.mjs.map +1 -1
- package/react-hooks/useMeasure.js.map +1 -1
- package/react-hooks/useMeasure.mjs.map +1 -1
- package/react-hooks/useObservableNextRouter.js.map +1 -1
- package/react-hooks/useObservableNextRouter.mjs.map +1 -1
- package/react-hooks/useObservableQuery.js.map +1 -1
- package/react-hooks/useObservableQuery.mjs.map +1 -1
- package/react-hooks/usePersistedObservable.d.ts +5 -3
- package/react-hooks/usePersistedObservable.js +5 -2
- package/react-hooks/usePersistedObservable.js.map +1 -1
- package/react-hooks/usePersistedObservable.mjs +5 -2
- package/react-hooks/usePersistedObservable.mjs.map +1 -1
- package/react.js +61 -75
- package/react.js.map +1 -1
- package/react.mjs +61 -75
- package/react.mjs.map +1 -1
- package/src/ObservableObject.ts +1184 -0
- package/src/ObservablePrimitive.ts +62 -0
- package/src/babel/index.ts +70 -0
- package/src/batching.ts +372 -0
- package/src/computed.ts +16 -0
- package/src/config/enable$get.ts +30 -0
- package/src/config/enableReactComponents.ts +26 -0
- package/src/config/enableReactNativeComponents.ts +102 -0
- package/src/config/enableReactTracking.ts +60 -0
- package/src/config/enableReactUse.ts +23 -0
- package/src/config/enable_peek.ts +31 -0
- package/src/config.ts +47 -0
- package/src/createObservable.ts +46 -0
- package/src/event.ts +26 -0
- package/src/globals.ts +224 -0
- package/src/helpers/fetch.ts +26 -0
- package/src/helpers/pageHash.ts +41 -0
- package/src/helpers/pageHashParams.ts +55 -0
- package/src/helpers/time.ts +30 -0
- package/src/helpers.ts +221 -0
- package/src/history/trackHistory.ts +29 -0
- package/src/is.ts +56 -0
- package/src/linked.ts +6 -0
- package/src/observable.ts +32 -0
- package/src/observableInterfaces.ts +165 -0
- package/src/observableTypes.ts +221 -0
- package/src/observe.ts +89 -0
- package/src/onChange.ts +136 -0
- package/src/persist/configureObservablePersistence.ts +7 -0
- package/src/persist/fieldTransformer.ts +149 -0
- package/src/persist/observablePersistRemoteFunctionsAdapter.ts +39 -0
- package/src/persist/persistObservable.ts +1029 -0
- package/src/persist-plugins/async-storage.ts +102 -0
- package/src/persist-plugins/fetch.ts +33 -0
- package/src/persist-plugins/firebase.ts +1050 -0
- package/src/persist-plugins/indexeddb.ts +433 -0
- package/src/persist-plugins/local-storage.ts +90 -0
- package/src/persist-plugins/mmkv.ts +90 -0
- package/src/persist-plugins/query.ts +133 -0
- package/src/persistTypes.ts +226 -0
- package/src/proxy.ts +28 -0
- package/src/react/Computed.tsx +7 -0
- package/src/react/For.tsx +116 -0
- package/src/react/Memo.tsx +4 -0
- package/src/react/Reactive.tsx +53 -0
- package/src/react/Show.tsx +33 -0
- package/src/react/Switch.tsx +43 -0
- package/src/react/react-globals.ts +3 -0
- package/src/react/{reactInterfaces.d.ts → reactInterfaces.ts} +15 -7
- package/src/react/reactive-observer.tsx +210 -0
- package/src/react/useComputed.ts +36 -0
- package/src/react/useEffectOnce.ts +41 -0
- package/src/react/useIsMounted.ts +16 -0
- package/src/react/useMount.ts +15 -0
- package/src/react/useObservable.ts +24 -0
- package/src/react/useObservableReducer.ts +52 -0
- package/src/react/useObservableState.ts +30 -0
- package/src/react/useObserve.ts +54 -0
- package/src/react/useObserveEffect.ts +40 -0
- package/src/react/usePauseProvider.tsx +13 -0
- package/src/react/useSelector.ts +167 -0
- package/src/react/useUnmount.ts +8 -0
- package/src/react/useWhen.ts +9 -0
- package/src/react-hooks/createObservableHook.ts +53 -0
- package/src/react-hooks/useFetch.ts +16 -0
- package/src/react-hooks/useHover.ts +40 -0
- package/src/react-hooks/useMeasure.ts +48 -0
- package/src/react-hooks/useObservableNextRouter.ts +137 -0
- package/src/react-hooks/useObservableQuery.ts +205 -0
- package/src/react-hooks/usePersistedObservable.ts +24 -0
- package/src/retry.ts +69 -0
- package/src/setupTracking.ts +26 -0
- package/src/sync/activateSyncedNode.ts +146 -0
- package/src/sync/configureObservableSync.ts +7 -0
- package/src/sync/syncHelpers.ts +15 -0
- package/src/sync/syncObservable.ts +989 -0
- package/src/sync/syncObservableAdapter.ts +30 -0
- package/src/sync/synced.ts +20 -0
- package/src/sync-plugins/fetch.ts +42 -0
- package/src/syncTypes.ts +163 -0
- package/src/trace/traceHelpers.ts +11 -0
- package/src/trace/useTraceListeners.ts +34 -0
- package/src/trace/useTraceUpdates.ts +24 -0
- package/src/trace/useVerifyNotTracking.ts +33 -0
- package/src/trace/useVerifyOneRender.ts +10 -0
- package/src/trackSelector.ts +52 -0
- package/src/tracking.ts +43 -0
- package/src/types/babel.d.ts +12 -0
- package/src/when.ts +70 -0
- package/sync-plugins/fetch.d.ts +11 -0
- package/sync-plugins/fetch.js +24 -0
- package/sync-plugins/fetch.js.map +1 -0
- package/sync-plugins/fetch.mjs +22 -0
- package/sync-plugins/fetch.mjs.map +1 -0
- package/sync.d.ts +8 -0
- package/sync.js +919 -0
- package/sync.js.map +1 -0
- package/sync.mjs +912 -0
- package/sync.mjs.map +1 -0
- package/trace.js +13 -10
- package/trace.js.map +1 -1
- package/trace.mjs +11 -8
- package/trace.mjs.map +1 -1
- package/types/babel.d.ts +3 -3
- package/config/enableDirectAccess.d.ts +0 -7
- package/config/enableDirectAccess.js +0 -25
- package/config/enableDirectAccess.js.map +0 -1
- package/config/enableDirectAccess.mjs +0 -23
- package/config/enableDirectAccess.mjs.map +0 -1
- package/config/enableDirectPeek.d.ts +0 -7
- package/config/enableDirectPeek.js.map +0 -1
- package/config/enableDirectPeek.mjs.map +0 -1
- package/config/enableReactDirectRender.d.ts +0 -2
- package/config/enableReactDirectRender.js +0 -78
- package/config/enableReactDirectRender.js.map +0 -1
- package/config/enableReactDirectRender.mjs +0 -75
- package/config/enableReactDirectRender.mjs.map +0 -1
- package/src/ObservableObject.d.ts +0 -14
- package/src/ObservablePrimitive.d.ts +0 -7
- package/src/babel/index.d.ts +0 -17
- package/src/batching.d.ts +0 -6
- package/src/computed.d.ts +0 -4
- package/src/config/enableDirectAccess.d.ts +0 -7
- package/src/config/enableDirectPeek.d.ts +0 -7
- package/src/config/enableReactComponents.d.ts +0 -7
- package/src/config/enableReactDirectRender.d.ts +0 -2
- package/src/config/enableReactNativeComponents.d.ts +0 -20
- package/src/config/enableReactTracking.d.ts +0 -15
- package/src/config/enableReactUse.d.ts +0 -7
- package/src/config.d.ts +0 -8
- package/src/createObservable.d.ts +0 -2
- package/src/event.d.ts +0 -2
- package/src/globals.d.ts +0 -32
- package/src/helpers/fetch.d.ts +0 -6
- package/src/helpers/pageHash.d.ts +0 -7
- package/src/helpers/pageHashParams.d.ts +0 -7
- package/src/helpers/time.d.ts +0 -3
- package/src/helpers.d.ts +0 -13
- package/src/history/trackHistory.d.ts +0 -4
- package/src/is.d.ts +0 -10
- package/src/observable.d.ts +0 -16
- package/src/observableInterfaces.d.ts +0 -456
- package/src/observe.d.ts +0 -6
- package/src/onChange.d.ts +0 -7
- package/src/persist/configureObservablePersistence.d.ts +0 -3
- package/src/persist/fieldTransformer.d.ts +0 -8
- package/src/persist/observablePersistRemoteFunctionsAdapter.d.ts +0 -2
- package/src/persist/persistActivateNode.d.ts +0 -1
- package/src/persist/persistHelpers.d.ts +0 -1
- package/src/persist/persistObservable.d.ts +0 -25
- package/src/persist-plugins/async-storage.d.ts +0 -14
- package/src/persist-plugins/fetch.d.ts +0 -10
- package/src/persist-plugins/firebase.d.ts +0 -51
- package/src/persist-plugins/indexeddb.d.ts +0 -25
- package/src/persist-plugins/local-storage.d.ts +0 -21
- package/src/persist-plugins/mmkv.d.ts +0 -14
- package/src/persist-plugins/query.d.ts +0 -18
- package/src/proxy.d.ts +0 -5
- package/src/react/Computed.d.ts +0 -5
- package/src/react/For.d.ts +0 -15
- package/src/react/Memo.d.ts +0 -3
- package/src/react/Reactive.d.ts +0 -9
- package/src/react/Show.d.ts +0 -18
- package/src/react/Switch.d.ts +0 -14
- package/src/react/react-globals.d.ts +0 -3
- package/src/react/reactive-observer.d.ts +0 -14
- package/src/react/useComputed.d.ts +0 -5
- package/src/react/useEffectOnce.d.ts +0 -1
- package/src/react/useIsMounted.d.ts +0 -2
- package/src/react/useMount.d.ts +0 -2
- package/src/react/useObservable.d.ts +0 -9
- package/src/react/useObservableReducer.d.ts +0 -7
- package/src/react/useObservableState.d.ts +0 -2
- package/src/react/useObserve.d.ts +0 -4
- package/src/react/useObserveEffect.d.ts +0 -4
- package/src/react/usePauseProvider.d.ts +0 -8
- package/src/react/useSelector.d.ts +0 -3
- package/src/react/useUnmount.d.ts +0 -2
- package/src/react/useWhen.d.ts +0 -3
- package/src/react-hooks/createObservableHook.d.ts +0 -2
- package/src/react-hooks/useFetch.d.ts +0 -6
- package/src/react-hooks/useHover.d.ts +0 -3
- package/src/react-hooks/useMeasure.d.ts +0 -6
- package/src/react-hooks/useObservableNextRouter.d.ts +0 -33
- package/src/react-hooks/useObservableQuery.d.ts +0 -6
- package/src/react-hooks/usePersistedObservable.d.ts +0 -11
- package/src/retry.d.ts +0 -9
- package/src/setupTracking.d.ts +0 -2
- package/src/trace/traceHelpers.d.ts +0 -2
- package/src/trace/useTraceListeners.d.ts +0 -1
- package/src/trace/useTraceUpdates.d.ts +0 -1
- package/src/trace/useVerifyNotTracking.d.ts +0 -1
- package/src/trace/useVerifyOneRender.d.ts +0 -1
- package/src/trackSelector.d.ts +0 -7
- package/src/tracking.d.ts +0 -13
- package/src/when.d.ts +0 -3
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ListenerParams,
|
|
3
|
+
Observable,
|
|
4
|
+
Selector,
|
|
5
|
+
computeSelector,
|
|
6
|
+
isObservable,
|
|
7
|
+
isPrimitive,
|
|
8
|
+
isPromise,
|
|
9
|
+
syncState,
|
|
10
|
+
trackSelector,
|
|
11
|
+
when,
|
|
12
|
+
} from '@legendapp/state';
|
|
13
|
+
import React, { useContext, useMemo } from 'react';
|
|
14
|
+
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
|
15
|
+
import { reactGlobals } from './react-globals';
|
|
16
|
+
import type { UseSelectorOptions } from './reactInterfaces';
|
|
17
|
+
import { PauseContext } from './usePauseProvider';
|
|
18
|
+
|
|
19
|
+
interface SelectorFunctions<T> {
|
|
20
|
+
subscribe: (onStoreChange: () => void) => () => void;
|
|
21
|
+
getVersion: () => number;
|
|
22
|
+
run: (selector: Selector<T>) => T;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createSelectorFunctions<T>(
|
|
26
|
+
options: UseSelectorOptions | undefined,
|
|
27
|
+
isPaused$: Observable<boolean>,
|
|
28
|
+
): SelectorFunctions<T> {
|
|
29
|
+
let version = 0;
|
|
30
|
+
let notify: () => void;
|
|
31
|
+
let dispose: (() => void) | undefined;
|
|
32
|
+
let resubscribe: (() => () => void) | undefined;
|
|
33
|
+
let _selector: Selector<T>;
|
|
34
|
+
let prev: T;
|
|
35
|
+
|
|
36
|
+
let pendingUpdate: any | undefined = undefined;
|
|
37
|
+
|
|
38
|
+
const run = () => {
|
|
39
|
+
// Dispose if already listening
|
|
40
|
+
dispose?.();
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
value,
|
|
44
|
+
dispose: _dispose,
|
|
45
|
+
resubscribe: _resubscribe,
|
|
46
|
+
} = trackSelector(_selector, _update, undefined, undefined, /*createResubscribe*/ true);
|
|
47
|
+
|
|
48
|
+
dispose = _dispose;
|
|
49
|
+
resubscribe = _resubscribe;
|
|
50
|
+
|
|
51
|
+
return value;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const _update = ({ value }: { value: ListenerParams['value'] }) => {
|
|
55
|
+
if (isPaused$?.peek()) {
|
|
56
|
+
const next = pendingUpdate;
|
|
57
|
+
pendingUpdate = value;
|
|
58
|
+
if (next === undefined) {
|
|
59
|
+
when(
|
|
60
|
+
() => !isPaused$.get(),
|
|
61
|
+
() => {
|
|
62
|
+
const latest = pendingUpdate;
|
|
63
|
+
pendingUpdate = undefined;
|
|
64
|
+
_update({ value: latest });
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
// If skipCheck then don't need to re-run selector
|
|
70
|
+
let changed = options?.skipCheck;
|
|
71
|
+
if (!changed) {
|
|
72
|
+
const newValue = run();
|
|
73
|
+
|
|
74
|
+
// If newValue is different than previous value then it's changed.
|
|
75
|
+
// Also if the selector returns an observable directly then its value will be the same as
|
|
76
|
+
// the value from the listener, and that should always re-render.
|
|
77
|
+
if (newValue !== prev || (!isPrimitive(newValue) && newValue === value)) {
|
|
78
|
+
changed = true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (changed) {
|
|
82
|
+
version++;
|
|
83
|
+
notify?.();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
subscribe: (onStoreChange: () => void) => {
|
|
90
|
+
notify = onStoreChange;
|
|
91
|
+
|
|
92
|
+
// Workaround for React 18 running twice in dev (part 2)
|
|
93
|
+
if (
|
|
94
|
+
(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
|
|
95
|
+
!dispose &&
|
|
96
|
+
resubscribe
|
|
97
|
+
) {
|
|
98
|
+
dispose = resubscribe();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return () => {
|
|
102
|
+
dispose?.();
|
|
103
|
+
dispose = undefined;
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
getVersion: () => version,
|
|
107
|
+
run: (selector: Selector<T>) => {
|
|
108
|
+
// Update the cached selector
|
|
109
|
+
_selector = selector;
|
|
110
|
+
|
|
111
|
+
return (prev = run());
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function useSelector<T>(selector: Selector<T>, options?: UseSelectorOptions): T {
|
|
117
|
+
// Short-circuit to skip creating the hook if selector is an observable
|
|
118
|
+
// and running in an observer. If selector is a function it needs to run in its own context.
|
|
119
|
+
if (reactGlobals.inObserver && isObservable(selector)) {
|
|
120
|
+
return computeSelector(selector);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let value;
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const isPaused$ = useContext(PauseContext);
|
|
127
|
+
const selectorFn = useMemo(() => createSelectorFunctions<T>(options, isPaused$), []);
|
|
128
|
+
const { subscribe, getVersion, run } = selectorFn;
|
|
129
|
+
|
|
130
|
+
// Run the selector
|
|
131
|
+
// Note: The selector needs to run on every render because it may have different results
|
|
132
|
+
// than the previous run if it uses local state
|
|
133
|
+
value = run(selector) as any;
|
|
134
|
+
|
|
135
|
+
useSyncExternalStore(subscribe, getVersion, getVersion);
|
|
136
|
+
|
|
137
|
+
// Suspense support
|
|
138
|
+
if (options?.suspense) {
|
|
139
|
+
// Note: Although it's not possible for an observable to be a promise, the selector may be a
|
|
140
|
+
// function that returns a Promise, so we handle that case too.
|
|
141
|
+
if (
|
|
142
|
+
isPromise(value) ||
|
|
143
|
+
(!value && isObservable(selector) && syncState(selector).isLoaded.get() === false)
|
|
144
|
+
) {
|
|
145
|
+
if (React.use) {
|
|
146
|
+
React.use(value);
|
|
147
|
+
} else {
|
|
148
|
+
throw value;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
} catch (err: unknown) {
|
|
153
|
+
if (
|
|
154
|
+
(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') &&
|
|
155
|
+
(err as Error)?.message?.includes('Rendered more')
|
|
156
|
+
) {
|
|
157
|
+
console.warn(
|
|
158
|
+
`[legend-state]: You may want to wrap this component in \`observer\` to fix the error of ${
|
|
159
|
+
(err as Error).message
|
|
160
|
+
}`,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Selector, when, whenReady } from '@legendapp/state';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
|
|
4
|
+
export function useWhen<T>(predicate: Selector<T>, effect: (value: T) => any | (() => any)) {
|
|
5
|
+
return useMemo(() => when(predicate, effect), []);
|
|
6
|
+
}
|
|
7
|
+
export function useWhenReady<T>(predicate: Selector<T>, effect: (value: T) => any | (() => any)) {
|
|
8
|
+
return useMemo(() => whenReady(predicate, effect), []);
|
|
9
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isFunction, Observable, observable } from '@legendapp/state';
|
|
2
|
+
import React, { MutableRefObject, Reducer, ReducerState } from 'react';
|
|
3
|
+
|
|
4
|
+
function overrideHooks<TRet>(refObs: MutableRefObject<Observable<TRet> | undefined>) {
|
|
5
|
+
// @ts-expect-error Types don't match React's expected types
|
|
6
|
+
React.useState = function useState(initialState: TRet | (() => TRet)) {
|
|
7
|
+
const obs =
|
|
8
|
+
refObs.current ??
|
|
9
|
+
(refObs.current = observable((isFunction(initialState) ? initialState() : initialState) as any) as any);
|
|
10
|
+
return [obs.get() as TRet, obs.set] as [TRet, React.Dispatch<React.SetStateAction<TRet>>];
|
|
11
|
+
};
|
|
12
|
+
// @ts-expect-error Types don't match React's expected types
|
|
13
|
+
React.useReducer = function useReducer<R extends Reducer<any, any>>(
|
|
14
|
+
reducer: R,
|
|
15
|
+
initializerArg: ReducerState<R>,
|
|
16
|
+
initializer: (arg: ReducerState<R>) => ReducerState<R>,
|
|
17
|
+
) {
|
|
18
|
+
const obs =
|
|
19
|
+
refObs.current ??
|
|
20
|
+
(refObs.current = observable(
|
|
21
|
+
initializerArg !== undefined && isFunction(initializerArg)
|
|
22
|
+
? initializer(initializerArg)
|
|
23
|
+
: initializerArg,
|
|
24
|
+
) as any);
|
|
25
|
+
const dispatch = (action: any) => {
|
|
26
|
+
obs.set(reducer(obs.get(), action));
|
|
27
|
+
};
|
|
28
|
+
return [obs, dispatch];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createObservableHook<TArgs extends any[], TRet>(
|
|
33
|
+
fn: (...args: TArgs) => TRet,
|
|
34
|
+
): (...args: TArgs) => Observable<TRet> {
|
|
35
|
+
const _useState = React.useState;
|
|
36
|
+
const _useReducer = React.useReducer;
|
|
37
|
+
|
|
38
|
+
return function (...args: TArgs) {
|
|
39
|
+
const refObs = React.useRef<Observable<TRet>>();
|
|
40
|
+
|
|
41
|
+
// First override the built-in hooks to create/update observables
|
|
42
|
+
overrideHooks(refObs);
|
|
43
|
+
|
|
44
|
+
// Then call the original hook
|
|
45
|
+
fn(...args);
|
|
46
|
+
|
|
47
|
+
// And reset back to the built-in hooks
|
|
48
|
+
React.useState = _useState;
|
|
49
|
+
React.useReducer = _useReducer;
|
|
50
|
+
|
|
51
|
+
return refObs.current as Observable<TRet>;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Observable } from '@legendapp/state';
|
|
2
|
+
import { observableFetch } from '@legendapp/state/helpers/fetch';
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
|
|
5
|
+
export function useFetch<T>(
|
|
6
|
+
input: RequestInfo | URL,
|
|
7
|
+
init?: RequestInit,
|
|
8
|
+
valueType?: 'arrayBuffer' | 'blob' | 'formData' | 'json' | 'text',
|
|
9
|
+
): Observable<{
|
|
10
|
+
data?: T;
|
|
11
|
+
error?: any;
|
|
12
|
+
errorStr?: string;
|
|
13
|
+
loading: boolean;
|
|
14
|
+
}> {
|
|
15
|
+
return useMemo(() => observableFetch<T>(input, init, valueType), []);
|
|
16
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Observable } from '@legendapp/state';
|
|
2
|
+
import { useObservable } from '@legendapp/state/react';
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
export function useHover<T extends HTMLElement>(ref: React.MutableRefObject<T>): Observable<boolean> {
|
|
6
|
+
const obs = useObservable(false);
|
|
7
|
+
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const handleMouseOver = () => obs.set(true);
|
|
10
|
+
const handleMouseOut = (e: MouseEvent) => {
|
|
11
|
+
if (obs.peek() === true) {
|
|
12
|
+
let parent = (e as any).toElement as HTMLElement | null;
|
|
13
|
+
let foundRef = false;
|
|
14
|
+
while (parent && !foundRef) {
|
|
15
|
+
if (parent === ref.current) {
|
|
16
|
+
foundRef = true;
|
|
17
|
+
}
|
|
18
|
+
parent = parent.parentElement;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!foundRef) {
|
|
22
|
+
obs.set(false);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const node = ref.current;
|
|
28
|
+
if (node) {
|
|
29
|
+
node.addEventListener('mouseover', handleMouseOver);
|
|
30
|
+
node.addEventListener('mouseout', handleMouseOut);
|
|
31
|
+
|
|
32
|
+
return () => {
|
|
33
|
+
node.removeEventListener('mouseover', handleMouseOver);
|
|
34
|
+
node.removeEventListener('mouseout', handleMouseOut);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}, [ref.current]);
|
|
38
|
+
|
|
39
|
+
return obs;
|
|
40
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ObservableObject } from '@legendapp/state';
|
|
2
|
+
import { useObservable } from '@legendapp/state/react';
|
|
3
|
+
import { RefObject, useLayoutEffect } from 'react';
|
|
4
|
+
|
|
5
|
+
function getSize(el: HTMLElement): { width: number; height: number } | undefined {
|
|
6
|
+
return el
|
|
7
|
+
? {
|
|
8
|
+
width: el.offsetWidth,
|
|
9
|
+
height: el.offsetHeight,
|
|
10
|
+
}
|
|
11
|
+
: undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function useMeasure(ref: RefObject<HTMLElement>): ObservableObject<{
|
|
15
|
+
width: number | undefined;
|
|
16
|
+
height: number | undefined;
|
|
17
|
+
}> {
|
|
18
|
+
const obs = useObservable<{ width: number | undefined; height: number | undefined }>({
|
|
19
|
+
width: undefined,
|
|
20
|
+
height: undefined,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
useLayoutEffect(() => {
|
|
24
|
+
const el = ref.current;
|
|
25
|
+
if (el) {
|
|
26
|
+
const handleResize = () => {
|
|
27
|
+
if (ref.current) {
|
|
28
|
+
const oldSize = obs.peek();
|
|
29
|
+
const newSize = getSize(ref.current);
|
|
30
|
+
if (newSize && (newSize.width !== oldSize.width || newSize.height !== oldSize.height)) {
|
|
31
|
+
obs.set(newSize);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
handleResize();
|
|
36
|
+
|
|
37
|
+
let resizeObserver = new ResizeObserver(handleResize);
|
|
38
|
+
resizeObserver.observe(el);
|
|
39
|
+
|
|
40
|
+
return () => {
|
|
41
|
+
resizeObserver.disconnect();
|
|
42
|
+
(resizeObserver as any) = undefined;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}, [ref.current]);
|
|
46
|
+
|
|
47
|
+
return obs;
|
|
48
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { isEmpty, observable, Observable, setSilently } from '@legendapp/state';
|
|
2
|
+
import Router, { NextRouter, useRouter } from 'next/router';
|
|
3
|
+
|
|
4
|
+
type ParsedUrlQuery = { [key: string]: string | string[] | undefined };
|
|
5
|
+
|
|
6
|
+
interface TransitionOptions {
|
|
7
|
+
shallow?: boolean;
|
|
8
|
+
locale?: string | false;
|
|
9
|
+
scroll?: boolean;
|
|
10
|
+
unstable_skipClientCache?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface ObservableNextRouterState {
|
|
13
|
+
pathname: string;
|
|
14
|
+
hash: string;
|
|
15
|
+
query: ParsedUrlQuery;
|
|
16
|
+
}
|
|
17
|
+
type RouteInfo = Partial<ObservableNextRouterState>;
|
|
18
|
+
export interface ParamsUseObservableNextRouterBase {
|
|
19
|
+
transitionOptions?: TransitionOptions;
|
|
20
|
+
method?: 'push' | 'replace';
|
|
21
|
+
subscribe?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface ParamsUseObservableNextRouter<T extends object> extends ParamsUseObservableNextRouterBase {
|
|
24
|
+
compute: (value: ObservableNextRouterState) => T;
|
|
25
|
+
set: (
|
|
26
|
+
value: T,
|
|
27
|
+
previous: T,
|
|
28
|
+
router: NextRouter,
|
|
29
|
+
) => RouteInfo & {
|
|
30
|
+
transitionOptions?: TransitionOptions;
|
|
31
|
+
method?: 'push' | 'replace';
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isShallowEqual(query1: ParsedUrlQuery, query2: ParsedUrlQuery) {
|
|
36
|
+
if (!query1 !== !query2) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const keys1 = Object.keys(query1);
|
|
40
|
+
const keys2 = Object.keys(query2);
|
|
41
|
+
|
|
42
|
+
if (keys1.length !== keys2.length) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const key of keys1) {
|
|
47
|
+
if (query1[key] !== query2[key]) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const routes$ = observable({});
|
|
56
|
+
let routeParams = {} as ParamsUseObservableNextRouter<any>;
|
|
57
|
+
let router: NextRouter;
|
|
58
|
+
|
|
59
|
+
routes$.onChange(({ value, getPrevious }) => {
|
|
60
|
+
// Only run this if being manually changed by the user
|
|
61
|
+
let setter = routeParams?.set;
|
|
62
|
+
if (!setter) {
|
|
63
|
+
if ((value as any).pathname) {
|
|
64
|
+
setter = () => value;
|
|
65
|
+
} else {
|
|
66
|
+
console.error('[legend-state]: Must provide a set method to useObservableNextRouter');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const setReturn = setter(value, getPrevious(), router);
|
|
70
|
+
const { pathname, hash, query } = setReturn;
|
|
71
|
+
let { transitionOptions, method } = setReturn;
|
|
72
|
+
|
|
73
|
+
method = method || routeParams?.method;
|
|
74
|
+
transitionOptions = transitionOptions || routeParams?.transitionOptions;
|
|
75
|
+
|
|
76
|
+
const prevHash = router.asPath.split('#')[1] || '';
|
|
77
|
+
|
|
78
|
+
const change: RouteInfo = {};
|
|
79
|
+
// Only include changes that were meant to be changed. For example the user may have
|
|
80
|
+
// only changed the hash so we don't need to push a pathname change.
|
|
81
|
+
if (pathname !== undefined && pathname !== router.pathname) {
|
|
82
|
+
change.pathname = pathname;
|
|
83
|
+
}
|
|
84
|
+
if (hash !== undefined && hash !== prevHash) {
|
|
85
|
+
change.hash = hash;
|
|
86
|
+
}
|
|
87
|
+
if (query !== undefined && !isShallowEqual(query, router.query)) {
|
|
88
|
+
change.query = query;
|
|
89
|
+
}
|
|
90
|
+
// Only push if there are changes
|
|
91
|
+
if (!isEmpty(change)) {
|
|
92
|
+
const fn = method === 'replace' ? 'replace' : 'push';
|
|
93
|
+
router[fn](change, undefined, transitionOptions).catch((e) => {
|
|
94
|
+
// workaround for https://github.com/vercel/next.js/issues/37362
|
|
95
|
+
if (!e.cancelled) throw e;
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export function useObservableNextRouter(): Observable<ObservableNextRouterState>;
|
|
101
|
+
export function useObservableNextRouter<T extends object>(params: ParamsUseObservableNextRouter<T>): Observable<T>;
|
|
102
|
+
export function useObservableNextRouter(
|
|
103
|
+
params: ParamsUseObservableNextRouterBase,
|
|
104
|
+
): Observable<ObservableNextRouterState>;
|
|
105
|
+
export function useObservableNextRouter<T extends object>(
|
|
106
|
+
params?: ParamsUseObservableNextRouter<T> | ParamsUseObservableNextRouterBase,
|
|
107
|
+
): Observable<T> | Observable<ObservableNextRouterState> {
|
|
108
|
+
const { subscribe, compute } = (params as ParamsUseObservableNextRouter<T>) || {};
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
// Use the useRouter hook if we're on the client side and want to subscribe to changes.
|
|
112
|
+
// Otherwise use the Router object so that this does not subscribe to router changes.
|
|
113
|
+
router = typeof window !== 'undefined' && !subscribe ? Router : useRouter();
|
|
114
|
+
} finally {
|
|
115
|
+
router = router || useRouter();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Update the local state with the new functions and options. This can happen when being run
|
|
119
|
+
// on a new page or if the user just changes it on the current page.
|
|
120
|
+
// It's better for performance than creating new observables or hooks for every use, since there may be
|
|
121
|
+
// many uses of useObservableRouter in the lifecycle of a page.
|
|
122
|
+
routeParams = params as ParamsUseObservableNextRouter<T>;
|
|
123
|
+
|
|
124
|
+
// Get the pathname and hash
|
|
125
|
+
const { asPath, pathname, query } = router;
|
|
126
|
+
const hash = asPath.split('#')[1] || '';
|
|
127
|
+
|
|
128
|
+
// Run the compute function to get the value of the object
|
|
129
|
+
const computeParams = { pathname, hash, query };
|
|
130
|
+
const obj = compute ? compute(computeParams) : computeParams;
|
|
131
|
+
|
|
132
|
+
// Set the object without triggering router.push
|
|
133
|
+
setSilently(routes$, obj);
|
|
134
|
+
|
|
135
|
+
// Return the observable with the computed values
|
|
136
|
+
return routes$ as any;
|
|
137
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// This is basically just React Query's useBaseQuery with a few changes for Legend-State:
|
|
2
|
+
// 1. Remove the useSyncExternalStore
|
|
3
|
+
// 2. Return an observable that subscribes to the query observer
|
|
4
|
+
// 3. If there is a mutator observe the observable for changes and call mutate
|
|
5
|
+
|
|
6
|
+
import { observable, observe, type ObservableObject } from '@legendapp/state';
|
|
7
|
+
import {
|
|
8
|
+
DefaultedQueryObserverOptions,
|
|
9
|
+
MutationObserver,
|
|
10
|
+
Query,
|
|
11
|
+
QueryClient,
|
|
12
|
+
QueryKey,
|
|
13
|
+
QueryObserver,
|
|
14
|
+
QueryObserverResult,
|
|
15
|
+
UseErrorBoundary,
|
|
16
|
+
notifyManager,
|
|
17
|
+
} from '@tanstack/query-core';
|
|
18
|
+
import {
|
|
19
|
+
UseBaseQueryOptions,
|
|
20
|
+
UseMutationOptions,
|
|
21
|
+
useIsRestoring,
|
|
22
|
+
useQueryClient,
|
|
23
|
+
useQueryErrorResetBoundary,
|
|
24
|
+
type UseBaseQueryResult,
|
|
25
|
+
} from '@tanstack/react-query';
|
|
26
|
+
import type { QueryErrorResetBoundaryValue } from '@tanstack/react-query/build/lib/QueryErrorResetBoundary';
|
|
27
|
+
import * as React from 'react';
|
|
28
|
+
|
|
29
|
+
const ensurePreventErrorBoundaryRetry = <TQueryFnData, TError, TData, TQueryData, TQueryKey extends QueryKey>(
|
|
30
|
+
options: DefaultedQueryObserverOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey>,
|
|
31
|
+
errorResetBoundary: QueryErrorResetBoundaryValue,
|
|
32
|
+
) => {
|
|
33
|
+
if (options.suspense || options.useErrorBoundary) {
|
|
34
|
+
// Prevent retrying failed query if the error boundary has not been reset yet
|
|
35
|
+
if (!errorResetBoundary.isReset()) {
|
|
36
|
+
options.retryOnMount = false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const useClearResetErrorBoundary = (errorResetBoundary: QueryErrorResetBoundaryValue) => {
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
errorResetBoundary.clearReset();
|
|
44
|
+
}, [errorResetBoundary]);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function shouldThrowError<T extends (...args: any[]) => boolean>(
|
|
48
|
+
_useErrorBoundary: boolean | T | undefined,
|
|
49
|
+
params: Parameters<T>,
|
|
50
|
+
): boolean {
|
|
51
|
+
// Allow useErrorBoundary function to override throwing behavior on a per-error basis
|
|
52
|
+
if (typeof _useErrorBoundary === 'function') {
|
|
53
|
+
return _useErrorBoundary(...params);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return !!_useErrorBoundary;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const getHasError = <TData, TError, TQueryFnData, TQueryData, TQueryKey extends QueryKey>({
|
|
60
|
+
result,
|
|
61
|
+
errorResetBoundary,
|
|
62
|
+
useErrorBoundary,
|
|
63
|
+
query,
|
|
64
|
+
}: {
|
|
65
|
+
result: QueryObserverResult<TData, TError>;
|
|
66
|
+
errorResetBoundary: QueryErrorResetBoundaryValue;
|
|
67
|
+
useErrorBoundary: UseErrorBoundary<TQueryFnData, TError, TQueryData, TQueryKey>;
|
|
68
|
+
query: Query<TQueryFnData, TError, TQueryData, TQueryKey>;
|
|
69
|
+
}) => {
|
|
70
|
+
return (
|
|
71
|
+
result.isError &&
|
|
72
|
+
!errorResetBoundary.isReset() &&
|
|
73
|
+
!result.isFetching &&
|
|
74
|
+
shouldThrowError(useErrorBoundary, [result.error, query])
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export function useObservableQuery<
|
|
79
|
+
TQueryFnData,
|
|
80
|
+
TError,
|
|
81
|
+
TData = TQueryFnData,
|
|
82
|
+
TQueryData = TQueryFnData,
|
|
83
|
+
TQueryKey extends QueryKey = QueryKey,
|
|
84
|
+
TContext = unknown,
|
|
85
|
+
>(
|
|
86
|
+
options: UseBaseQueryOptions<TQueryFnData, TError, TData, TQueryData, TQueryKey> & { queryClient?: QueryClient },
|
|
87
|
+
mutationOptions?: UseMutationOptions<TData, TError, void, TContext>,
|
|
88
|
+
): ObservableObject<UseBaseQueryResult<TData, TError>> {
|
|
89
|
+
const Observer = QueryObserver;
|
|
90
|
+
const queryClient = options?.queryClient || useQueryClient({ context: options.context });
|
|
91
|
+
const isRestoring = useIsRestoring();
|
|
92
|
+
const errorResetBoundary = useQueryErrorResetBoundary();
|
|
93
|
+
const defaultedOptions = queryClient.defaultQueryOptions(options);
|
|
94
|
+
|
|
95
|
+
// Make sure results are optimistically set in fetching state before subscribing or updating options
|
|
96
|
+
defaultedOptions._optimisticResults = isRestoring ? 'isRestoring' : 'optimistic';
|
|
97
|
+
|
|
98
|
+
// Include callbacks in batch renders
|
|
99
|
+
if (defaultedOptions.onError) {
|
|
100
|
+
defaultedOptions.onError = notifyManager.batchCalls(defaultedOptions.onError);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (defaultedOptions.onSuccess) {
|
|
104
|
+
defaultedOptions.onSuccess = notifyManager.batchCalls(defaultedOptions.onSuccess);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (defaultedOptions.onSettled) {
|
|
108
|
+
defaultedOptions.onSettled = notifyManager.batchCalls(defaultedOptions.onSettled);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (defaultedOptions.suspense) {
|
|
112
|
+
// Always set stale time when using suspense to prevent
|
|
113
|
+
// fetching again when directly mounting after suspending
|
|
114
|
+
if (typeof defaultedOptions.staleTime !== 'number') {
|
|
115
|
+
defaultedOptions.staleTime = 1000;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
ensurePreventErrorBoundaryRetry(defaultedOptions, errorResetBoundary);
|
|
120
|
+
|
|
121
|
+
useClearResetErrorBoundary(errorResetBoundary);
|
|
122
|
+
|
|
123
|
+
const [observer] = React.useState(
|
|
124
|
+
() => new Observer<TQueryFnData, TError, TData, TQueryData, TQueryKey>(queryClient, defaultedOptions),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const result = observer.getOptimisticResult(defaultedOptions);
|
|
128
|
+
|
|
129
|
+
// useSyncExternalStore was here in useBaseQuery but is removed for Legend-State.
|
|
130
|
+
|
|
131
|
+
React.useEffect(() => {
|
|
132
|
+
// Do not notify on updates because of changes in the options because
|
|
133
|
+
// these changes should already be reflected in the optimistic result.
|
|
134
|
+
observer.setOptions(defaultedOptions, { listeners: false });
|
|
135
|
+
}, [defaultedOptions, observer]);
|
|
136
|
+
|
|
137
|
+
// Handle suspense
|
|
138
|
+
if (defaultedOptions.suspense && result.isLoading && result.isFetching && !isRestoring) {
|
|
139
|
+
throw observer
|
|
140
|
+
.fetchOptimistic(defaultedOptions)
|
|
141
|
+
.then(({ data }) => {
|
|
142
|
+
defaultedOptions.onSuccess?.(data as TData);
|
|
143
|
+
defaultedOptions.onSettled?.(data, null);
|
|
144
|
+
})
|
|
145
|
+
.catch((error) => {
|
|
146
|
+
errorResetBoundary.clearReset();
|
|
147
|
+
defaultedOptions.onError?.(error);
|
|
148
|
+
defaultedOptions.onSettled?.(undefined, error);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Handle error boundary
|
|
153
|
+
if (
|
|
154
|
+
getHasError({
|
|
155
|
+
result,
|
|
156
|
+
errorResetBoundary,
|
|
157
|
+
useErrorBoundary: defaultedOptions.useErrorBoundary,
|
|
158
|
+
query: observer.getCurrentQuery(),
|
|
159
|
+
})
|
|
160
|
+
) {
|
|
161
|
+
throw result.error;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Legend-State changes from here down
|
|
165
|
+
let mutator: MutationObserver<TData, TError, void, TContext>;
|
|
166
|
+
if (mutationOptions) {
|
|
167
|
+
[mutator] = React.useState(() => new MutationObserver(queryClient, mutationOptions));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const [obs] = React.useState<ObservableObject<UseBaseQueryResult<TData, TError>>>(() => {
|
|
171
|
+
const obs = observable<any>(observer.getCurrentResult());
|
|
172
|
+
|
|
173
|
+
let isSetting = false;
|
|
174
|
+
|
|
175
|
+
// If there is a mutator watch for changes as long as they don't come from the the query observer
|
|
176
|
+
if (mutationOptions) {
|
|
177
|
+
observe(() => {
|
|
178
|
+
const data = (obs as any).data.get();
|
|
179
|
+
// Don't want to call mutate if there's no data or this coming from the query changing
|
|
180
|
+
if (data && !isSetting) {
|
|
181
|
+
mutator.mutate(data);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Note: Don't need to worry about unsubscribing because the query observer itself
|
|
187
|
+
// is scoped to this component
|
|
188
|
+
observer.subscribe((result) => {
|
|
189
|
+
isSetting = true;
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
// Update the observable with the latest value
|
|
193
|
+
obs.set(result);
|
|
194
|
+
} finally {
|
|
195
|
+
// If set causes a crash for some reason we still need to reset isSetting
|
|
196
|
+
isSetting = false;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
return obs as unknown as ObservableObject<UseBaseQueryResult<TData, TError>>;
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Return the observable
|
|
204
|
+
return obs;
|
|
205
|
+
}
|