@octanejs/apollo-client 0.1.0
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/LICENSE +49 -0
- package/README.md +97 -0
- package/package.json +77 -0
- package/src/index.d.ts +1 -0
- package/src/index.js +3 -0
- package/src/react/context/ApolloContext.d.ts +8 -0
- package/src/react/context/ApolloContext.js +21 -0
- package/src/react/context/ApolloProvider.d.ts +9 -0
- package/src/react/context/ApolloProvider.js +15 -0
- package/src/react/hooks/constants.d.ts +2 -0
- package/src/react/hooks/constants.js +1 -0
- package/src/react/hooks/internal/__use.d.ts +3 -0
- package/src/react/hooks/internal/__use.js +9 -0
- package/src/react/hooks/internal/index.d.ts +5 -0
- package/src/react/hooks/internal/index.js +6 -0
- package/src/react/hooks/internal/site.d.ts +2 -0
- package/src/react/hooks/internal/site.js +20 -0
- package/src/react/hooks/internal/useDeepMemo.d.ts +5 -0
- package/src/react/hooks/internal/useDeepMemo.js +11 -0
- package/src/react/hooks/internal/useIsomorphicLayoutEffect.d.ts +2 -0
- package/src/react/hooks/internal/useIsomorphicLayoutEffect.js +8 -0
- package/src/react/hooks/internal/useRenderGuard.d.ts +1 -0
- package/src/react/hooks/internal/useRenderGuard.js +14 -0
- package/src/react/hooks/internal/useSuspenseHookCacheKey.d.ts +13 -0
- package/src/react/hooks/internal/useSuspenseHookCacheKey.js +19 -0
- package/src/react/hooks/internal/validateSuspenseHookOptions.d.ts +4 -0
- package/src/react/hooks/internal/validateSuspenseHookOptions.js +16 -0
- package/src/react/hooks/internal/wrapHook.d.ts +77 -0
- package/src/react/hooks/internal/wrapHook.js +64 -0
- package/src/react/hooks/useApolloClient.d.ts +17 -0
- package/src/react/hooks/useApolloClient.js +27 -0
- package/src/react/hooks/useBackgroundQuery.d.ts +3061 -0
- package/src/react/hooks/useBackgroundQuery.js +88 -0
- package/src/react/hooks/useFragment.d.ts +205 -0
- package/src/react/hooks/useFragment.js +75 -0
- package/src/react/hooks/useLazyQuery.d.ts +947 -0
- package/src/react/hooks/useLazyQuery.js +156 -0
- package/src/react/hooks/useLoadableQuery.d.ts +955 -0
- package/src/react/hooks/useLoadableQuery.js +98 -0
- package/src/react/hooks/useMutation.d.ts +585 -0
- package/src/react/hooks/useMutation.js +121 -0
- package/src/react/hooks/useQuery.d.ts +1401 -0
- package/src/react/hooks/useQuery.js +234 -0
- package/src/react/hooks/useQueryRefHandlers.d.ts +85 -0
- package/src/react/hooks/useQueryRefHandlers.js +79 -0
- package/src/react/hooks/useReactiveVar.d.ts +21 -0
- package/src/react/hooks/useReactiveVar.js +41 -0
- package/src/react/hooks/useReadQuery.d.ts +104 -0
- package/src/react/hooks/useReadQuery.js +83 -0
- package/src/react/hooks/useSubscription.d.ts +289 -0
- package/src/react/hooks/useSubscription.js +258 -0
- package/src/react/hooks/useSuspenseFragment.d.ts +154 -0
- package/src/react/hooks/useSuspenseFragment.js +50 -0
- package/src/react/hooks/useSuspenseQuery.d.ts +1629 -0
- package/src/react/hooks/useSuspenseQuery.js +130 -0
- package/src/react/hooks/useSyncExternalStore.d.ts +2 -0
- package/src/react/hooks/useSyncExternalStore.js +125 -0
- package/src/react/index.d.ts +68 -0
- package/src/react/index.js +18 -0
- package/src/react/internal/cache/FragmentReference.d.ts +41 -0
- package/src/react/internal/cache/FragmentReference.js +117 -0
- package/src/react/internal/cache/QueryReference.d.ts +180 -0
- package/src/react/internal/cache/QueryReference.js +310 -0
- package/src/react/internal/cache/SuspenseCache.d.ts +38 -0
- package/src/react/internal/cache/SuspenseCache.js +39 -0
- package/src/react/internal/cache/getSuspenseCache.d.ts +26 -0
- package/src/react/internal/cache/getSuspenseCache.js +8 -0
- package/src/react/internal/cache/types.d.ts +13 -0
- package/src/react/internal/cache/types.js +1 -0
- package/src/react/internal/index.d.ts +16 -0
- package/src/react/internal/index.js +10 -0
- package/src/react/internal/types.d.ts +15 -0
- package/src/react/internal/types.js +1 -0
- package/src/react/query-preloader/createQueryPreloader.d.ts +630 -0
- package/src/react/query-preloader/createQueryPreloader.js +89 -0
- package/src/react/types/deprecated.d.ts +140 -0
- package/src/react/types/deprecated.js +1 -0
- package/src/react/types/types.documentation.d.ts +551 -0
- package/src/react/types/types.documentation.js +1 -0
- package/src/testing/index.d.ts +1 -0
- package/src/testing/index.js +1 -0
- package/src/testing/react/MockedProvider.d.ts +21 -0
- package/src/testing/react/MockedProvider.js +53 -0
- package/src/testing/react/index.d.ts +2 -0
- package/src/testing/react/index.js +1 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function parameters in this file try to follow a common order for the sake of
|
|
3
|
+
* readability and consistency. The order is as follows:
|
|
4
|
+
*
|
|
5
|
+
* resultData
|
|
6
|
+
* observable
|
|
7
|
+
* client
|
|
8
|
+
* query
|
|
9
|
+
* options
|
|
10
|
+
* watchQueryOptions
|
|
11
|
+
* makeWatchQueryOptions
|
|
12
|
+
*/
|
|
13
|
+
/** */
|
|
14
|
+
import { equal } from '@wry/equality';
|
|
15
|
+
import * as React from 'octane';
|
|
16
|
+
import { asapScheduler, observeOn } from 'rxjs';
|
|
17
|
+
import { NetworkStatus } from '@apollo/client';
|
|
18
|
+
import {
|
|
19
|
+
maybeDeepFreeze,
|
|
20
|
+
mergeOptions,
|
|
21
|
+
variablesUnknownSymbol,
|
|
22
|
+
} from '@apollo/client/utilities/internal';
|
|
23
|
+
import { skipToken } from './constants.js';
|
|
24
|
+
import { useDeepMemo, wrapHook } from './internal/index.js';
|
|
25
|
+
import { isCompilerSite } from './internal/site.js';
|
|
26
|
+
import { useApolloClient } from './useApolloClient.js';
|
|
27
|
+
import { useSyncExternalStore } from './useSyncExternalStore.js';
|
|
28
|
+
const lastWatchOptions = Symbol();
|
|
29
|
+
export const useQuery = function useQuery(query, options) {
|
|
30
|
+
'use no memo';
|
|
31
|
+
if (isCompilerSite(options, skipToken)) options = undefined;
|
|
32
|
+
return wrapHook(
|
|
33
|
+
'useQuery',
|
|
34
|
+
useQuery_,
|
|
35
|
+
useApolloClient(typeof options === 'object' ? options.client : undefined),
|
|
36
|
+
)(query, options);
|
|
37
|
+
};
|
|
38
|
+
function useQuery_(query, options = {}) {
|
|
39
|
+
const client = useApolloClient(typeof options === 'object' ? options.client : undefined);
|
|
40
|
+
const { ssr } = typeof options === 'object' ? options : {};
|
|
41
|
+
const watchQueryOptions = useOptions(query, options, client.defaultOptions.watchQuery);
|
|
42
|
+
function createState(previous) {
|
|
43
|
+
const observable = client.watchQuery(watchQueryOptions);
|
|
44
|
+
return {
|
|
45
|
+
client,
|
|
46
|
+
query,
|
|
47
|
+
observable,
|
|
48
|
+
resultData: {
|
|
49
|
+
current: observable.getCurrentResult(),
|
|
50
|
+
// Reuse previousData from previous InternalState (if any) to provide
|
|
51
|
+
// continuity of previousData even if/when the query or client changes.
|
|
52
|
+
previousData: previous?.resultData.current.data,
|
|
53
|
+
variables: observable.variables,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
let [state, setState] = React.useState(createState);
|
|
58
|
+
if (client !== state.client || query !== state.query) {
|
|
59
|
+
// If the client or query have changed, we need to create a new InternalState.
|
|
60
|
+
// This will trigger a re-render with the new state, but it will also continue
|
|
61
|
+
// to run the current render function to completion.
|
|
62
|
+
// Since we sometimes trigger some side-effects in the render function, we
|
|
63
|
+
// re-assign `state` to the new state to ensure that those side-effects are
|
|
64
|
+
// triggered with the new state.
|
|
65
|
+
setState((state = createState(state)));
|
|
66
|
+
}
|
|
67
|
+
const { observable, resultData } = state;
|
|
68
|
+
useInitialFetchPolicyIfNecessary(watchQueryOptions, observable);
|
|
69
|
+
useResubscribeIfNecessary(
|
|
70
|
+
resultData, // might get mutated during render
|
|
71
|
+
observable, // might get mutated during render
|
|
72
|
+
watchQueryOptions,
|
|
73
|
+
);
|
|
74
|
+
const result = useResult(observable, resultData, ssr);
|
|
75
|
+
const obsQueryFields = React.useMemo(
|
|
76
|
+
() => ({
|
|
77
|
+
refetch: observable.refetch.bind(observable),
|
|
78
|
+
fetchMore: observable.fetchMore.bind(observable),
|
|
79
|
+
updateQuery: observable.updateQuery.bind(observable),
|
|
80
|
+
startPolling: observable.startPolling.bind(observable),
|
|
81
|
+
stopPolling: observable.stopPolling.bind(observable),
|
|
82
|
+
subscribeToMore: observable.subscribeToMore.bind(observable),
|
|
83
|
+
}),
|
|
84
|
+
[observable],
|
|
85
|
+
);
|
|
86
|
+
const previousData = resultData.previousData;
|
|
87
|
+
return React.useMemo(() => {
|
|
88
|
+
const { partial, ...rest } = result;
|
|
89
|
+
return {
|
|
90
|
+
...rest,
|
|
91
|
+
client,
|
|
92
|
+
observable,
|
|
93
|
+
variables: observable.variables,
|
|
94
|
+
previousData,
|
|
95
|
+
...obsQueryFields,
|
|
96
|
+
};
|
|
97
|
+
}, [result, client, observable, previousData, obsQueryFields]);
|
|
98
|
+
}
|
|
99
|
+
const fromSkipToken = Symbol();
|
|
100
|
+
function useOptions(query, options, defaultOptions) {
|
|
101
|
+
return useDeepMemo(() => {
|
|
102
|
+
if (options === skipToken) {
|
|
103
|
+
const opts = {
|
|
104
|
+
...mergeOptions(defaultOptions, {
|
|
105
|
+
query,
|
|
106
|
+
fetchPolicy: 'standby',
|
|
107
|
+
}),
|
|
108
|
+
[variablesUnknownSymbol]: true,
|
|
109
|
+
};
|
|
110
|
+
opts[fromSkipToken] = true;
|
|
111
|
+
return opts;
|
|
112
|
+
}
|
|
113
|
+
const watchQueryOptions = mergeOptions(defaultOptions, { ...options, query });
|
|
114
|
+
if (options.skip) {
|
|
115
|
+
watchQueryOptions.initialFetchPolicy = options.initialFetchPolicy || options.fetchPolicy;
|
|
116
|
+
watchQueryOptions.fetchPolicy = 'standby';
|
|
117
|
+
}
|
|
118
|
+
return watchQueryOptions;
|
|
119
|
+
}, [query, options, defaultOptions]);
|
|
120
|
+
}
|
|
121
|
+
function useInitialFetchPolicyIfNecessary(watchQueryOptions, observable) {
|
|
122
|
+
'use no memo';
|
|
123
|
+
if (!watchQueryOptions.fetchPolicy) {
|
|
124
|
+
watchQueryOptions.fetchPolicy = observable.options.initialFetchPolicy;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function useResult(observable, resultData, ssr) {
|
|
128
|
+
'use no memo';
|
|
129
|
+
const fetchPolicy = observable.options.fetchPolicy;
|
|
130
|
+
return useSyncExternalStore(
|
|
131
|
+
React.useCallback(
|
|
132
|
+
(handleStoreChange) => {
|
|
133
|
+
const subscription = observable
|
|
134
|
+
// We use the asapScheduler here to prevent issues with trying to
|
|
135
|
+
// update in the middle of a render. `reobserve` is kicked off in the
|
|
136
|
+
// middle of a render and because RxJS emits values synchronously,
|
|
137
|
+
// its possible for this `handleStoreChange` to be called in that same
|
|
138
|
+
// render. This allows the render to complete before trying to emit a
|
|
139
|
+
// new value.
|
|
140
|
+
.pipe(observeOn(asapScheduler))
|
|
141
|
+
.subscribe((result) => {
|
|
142
|
+
const previous = resultData.current;
|
|
143
|
+
if (
|
|
144
|
+
// Avoid rerendering if the result is the same
|
|
145
|
+
equal(previous, result) &&
|
|
146
|
+
// Force rerender if the value was emitted because variables
|
|
147
|
+
// changed, such as when calling `refetch(newVars)` which returns
|
|
148
|
+
// the same data when `notifyOnNetworkStatusChange` is `false`.
|
|
149
|
+
equal(resultData.variables, observable.variables)
|
|
150
|
+
) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
resultData.variables = observable.variables;
|
|
154
|
+
if (previous.data && !equal(previous.data, result.data)) {
|
|
155
|
+
resultData.previousData = previous.data;
|
|
156
|
+
}
|
|
157
|
+
resultData.current = result;
|
|
158
|
+
handleStoreChange();
|
|
159
|
+
});
|
|
160
|
+
// Do the "unsubscribe" with a short delay.
|
|
161
|
+
// This way, an existing subscription can be reused without an additional
|
|
162
|
+
// request if "unsubscribe" and "resubscribe" to the same ObservableQuery
|
|
163
|
+
// happen in very fast succession.
|
|
164
|
+
return () => {
|
|
165
|
+
setTimeout(() => subscription.unsubscribe());
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
[observable, resultData],
|
|
169
|
+
),
|
|
170
|
+
() => resultData.current,
|
|
171
|
+
() =>
|
|
172
|
+
(fetchPolicy !== 'standby' && ssr === false) || fetchPolicy === 'no-cache'
|
|
173
|
+
? useQuery.ssrDisabledResult
|
|
174
|
+
: resultData.current,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
// this hook is not compatible with any rules of React, and there's no good way to rewrite it.
|
|
178
|
+
// it should stay a separate hook that will not be optimized by the compiler
|
|
179
|
+
function useResubscribeIfNecessary(
|
|
180
|
+
/** this hook will mutate properties on `resultData` */
|
|
181
|
+
resultData,
|
|
182
|
+
/** this hook will mutate properties on `observable` */
|
|
183
|
+
observable,
|
|
184
|
+
watchQueryOptions,
|
|
185
|
+
) {
|
|
186
|
+
'use no memo';
|
|
187
|
+
if (observable[lastWatchOptions] && !equal(observable[lastWatchOptions], watchQueryOptions)) {
|
|
188
|
+
// If skipToken was used to generate options, we won't know the correct
|
|
189
|
+
// initialFetchPolicy until the hook is rerendered with real options, so we
|
|
190
|
+
// set it the next time we get real options
|
|
191
|
+
if (observable[lastWatchOptions][fromSkipToken] && !watchQueryOptions.initialFetchPolicy) {
|
|
192
|
+
watchQueryOptions.initialFetchPolicy = watchQueryOptions.fetchPolicy;
|
|
193
|
+
}
|
|
194
|
+
// Though it might be tempting to postpone this reobserve call to the
|
|
195
|
+
// useEffect block, we need getCurrentResult to return an appropriate
|
|
196
|
+
// loading:true result synchronously (later within the same call to
|
|
197
|
+
// useQuery). Since we already have this.observable here (not true for
|
|
198
|
+
// the very first call to useQuery), we are not initiating any new
|
|
199
|
+
// subscriptions, though it does feel less than ideal that reobserve
|
|
200
|
+
// (potentially) kicks off a network request (for example, when the
|
|
201
|
+
// variables have changed), which is technically a side-effect.
|
|
202
|
+
if (shouldReobserve(observable[lastWatchOptions], watchQueryOptions)) {
|
|
203
|
+
observable.reobserve(watchQueryOptions);
|
|
204
|
+
} else {
|
|
205
|
+
observable.applyOptions(watchQueryOptions);
|
|
206
|
+
}
|
|
207
|
+
// Make sure getCurrentResult returns a fresh ApolloQueryResult<TData>,
|
|
208
|
+
// but save the current data as this.previousData, just like setResult
|
|
209
|
+
// usually does.
|
|
210
|
+
const result = observable.getCurrentResult();
|
|
211
|
+
if (!equal(result.data, resultData.current.data)) {
|
|
212
|
+
resultData.previousData = resultData.current.data || resultData.previousData;
|
|
213
|
+
}
|
|
214
|
+
resultData.current = result;
|
|
215
|
+
resultData.variables = observable.variables;
|
|
216
|
+
}
|
|
217
|
+
observable[lastWatchOptions] = watchQueryOptions;
|
|
218
|
+
}
|
|
219
|
+
function shouldReobserve(previousOptions, options) {
|
|
220
|
+
return (
|
|
221
|
+
previousOptions.query !== options.query ||
|
|
222
|
+
!equal(previousOptions.variables, options.variables) ||
|
|
223
|
+
(previousOptions.fetchPolicy !== options.fetchPolicy &&
|
|
224
|
+
(options.fetchPolicy === 'standby' || previousOptions.fetchPolicy === 'standby'))
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
useQuery.ssrDisabledResult = maybeDeepFreeze({
|
|
228
|
+
loading: true,
|
|
229
|
+
data: void 0,
|
|
230
|
+
dataState: 'empty',
|
|
231
|
+
error: void 0,
|
|
232
|
+
networkStatus: NetworkStatus.loading,
|
|
233
|
+
partial: true,
|
|
234
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { DataState, OperationVariables } from '@apollo/client';
|
|
2
|
+
import type { SubscribeToMoreFunction } from '@apollo/client';
|
|
3
|
+
import type { QueryRef } from '@octanejs/apollo-client/react';
|
|
4
|
+
import type { FetchMoreFunction, RefetchFunction } from '@octanejs/apollo-client/react/internal';
|
|
5
|
+
export declare namespace useQueryRefHandlers {
|
|
6
|
+
interface Result<TData = unknown, TVariables extends OperationVariables = OperationVariables> {
|
|
7
|
+
/**
|
|
8
|
+
* Update the variables of this observable query, and fetch the new results.
|
|
9
|
+
* This method should be preferred over `setVariables` in most use cases.
|
|
10
|
+
*
|
|
11
|
+
* Returns a `ResultPromise` with an additional `.retain()` method. Calling
|
|
12
|
+
* `.retain()` keeps the network operation running even if the `ObservableQuery`
|
|
13
|
+
* no longer requires the result.
|
|
14
|
+
*
|
|
15
|
+
* Note: `refetch()` guarantees that a value will be emitted from the
|
|
16
|
+
* observable, even if the result is deep equal to the previous value.
|
|
17
|
+
*
|
|
18
|
+
* @param variables - The new set of variables. If there are missing variables,
|
|
19
|
+
* the previous values of those variables will be used.
|
|
20
|
+
*/
|
|
21
|
+
refetch: RefetchFunction<TData, TVariables>;
|
|
22
|
+
/**
|
|
23
|
+
* A function that helps you fetch the next set of results for a [paginated list field](https://www.apollographql.com/docs/react/pagination/core-api/).
|
|
24
|
+
*/
|
|
25
|
+
fetchMore: FetchMoreFunction<TData, TVariables>;
|
|
26
|
+
/**
|
|
27
|
+
* A function that enables you to execute a [subscription](https://www.apollographql.com/docs/react/data/subscriptions/), usually to subscribe to specific fields that were included in the query.
|
|
28
|
+
*
|
|
29
|
+
* This function returns _another_ function that you can call to terminate the subscription.
|
|
30
|
+
*/
|
|
31
|
+
subscribeToMore: SubscribeToMoreFunction<TData, TVariables>;
|
|
32
|
+
}
|
|
33
|
+
namespace DocumentationTypes {
|
|
34
|
+
/**
|
|
35
|
+
* A React hook that returns a `refetch` and `fetchMore` function for a given
|
|
36
|
+
* `queryRef`.
|
|
37
|
+
*
|
|
38
|
+
* This is useful to get access to handlers for a `queryRef` that was created by
|
|
39
|
+
* `createQueryPreloader` or when the handlers for a `queryRef` produced in
|
|
40
|
+
* a different component are inaccessible.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
*
|
|
44
|
+
* ```tsx
|
|
45
|
+
* const MyComponent({ queryRef }) {
|
|
46
|
+
* const { refetch, fetchMore } = useQueryRefHandlers(queryRef);
|
|
47
|
+
*
|
|
48
|
+
* // ...
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @param queryRef - A `QueryRef` returned from `useBackgroundQuery`, `useLoadableQuery`, or `createQueryPreloader`.
|
|
53
|
+
*/
|
|
54
|
+
function useQueryRefHandlers<
|
|
55
|
+
TData = unknown,
|
|
56
|
+
TVariables extends OperationVariables = OperationVariables,
|
|
57
|
+
>(queryRef: QueryRef<TData, TVariables>): useQueryRefHandlers.Result<TData, TVariables>;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A React hook that returns a `refetch` and `fetchMore` function for a given
|
|
62
|
+
* `queryRef`.
|
|
63
|
+
*
|
|
64
|
+
* This is useful to get access to handlers for a `queryRef` that was created by
|
|
65
|
+
* `createQueryPreloader` or when the handlers for a `queryRef` produced in
|
|
66
|
+
* a different component are inaccessible.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
*
|
|
70
|
+
* ```tsx
|
|
71
|
+
* const MyComponent({ queryRef }) {
|
|
72
|
+
* const { refetch, fetchMore } = useQueryRefHandlers(queryRef);
|
|
73
|
+
*
|
|
74
|
+
* // ...
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @param queryRef - A `QueryRef` returned from `useBackgroundQuery`, `useLoadableQuery`, or `createQueryPreloader`.
|
|
79
|
+
*/
|
|
80
|
+
export declare function useQueryRefHandlers<
|
|
81
|
+
TData = unknown,
|
|
82
|
+
TVariables extends OperationVariables = OperationVariables,
|
|
83
|
+
>(
|
|
84
|
+
queryRef: QueryRef<TData, TVariables, DataState<TData>['dataState']>,
|
|
85
|
+
): useQueryRefHandlers.Result<TData, TVariables>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import * as React from 'octane';
|
|
2
|
+
import {
|
|
3
|
+
assertWrappedQueryRef,
|
|
4
|
+
getWrappedPromise,
|
|
5
|
+
unwrapQueryRef,
|
|
6
|
+
updateWrappedQueryRef,
|
|
7
|
+
wrapQueryRef,
|
|
8
|
+
} from '@octanejs/apollo-client/react/internal';
|
|
9
|
+
import { wrapHook } from './internal/index.js';
|
|
10
|
+
import { useApolloClient } from './useApolloClient.js';
|
|
11
|
+
/**
|
|
12
|
+
* A React hook that returns a `refetch` and `fetchMore` function for a given
|
|
13
|
+
* `queryRef`.
|
|
14
|
+
*
|
|
15
|
+
* This is useful to get access to handlers for a `queryRef` that was created by
|
|
16
|
+
* `createQueryPreloader` or when the handlers for a `queryRef` produced in
|
|
17
|
+
* a different component are inaccessible.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
*
|
|
21
|
+
* ```tsx
|
|
22
|
+
* const MyComponent({ queryRef }) {
|
|
23
|
+
* const { refetch, fetchMore } = useQueryRefHandlers(queryRef);
|
|
24
|
+
*
|
|
25
|
+
* // ...
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @param queryRef - A `QueryRef` returned from `useBackgroundQuery`, `useLoadableQuery`, or `createQueryPreloader`.
|
|
30
|
+
*/
|
|
31
|
+
export function useQueryRefHandlers(queryRef) {
|
|
32
|
+
'use no memo';
|
|
33
|
+
const unwrapped = unwrapQueryRef(queryRef);
|
|
34
|
+
const clientOrObsQuery = useApolloClient(
|
|
35
|
+
unwrapped
|
|
36
|
+
? // passing an `ObservableQuery` is not supported by the types, but it will
|
|
37
|
+
// return any truthy value that is passed in as an override so we cast the result
|
|
38
|
+
unwrapped['observable']
|
|
39
|
+
: undefined,
|
|
40
|
+
);
|
|
41
|
+
return wrapHook('useQueryRefHandlers', useQueryRefHandlers_, clientOrObsQuery)(queryRef);
|
|
42
|
+
}
|
|
43
|
+
function useQueryRefHandlers_(queryRef) {
|
|
44
|
+
assertWrappedQueryRef(queryRef);
|
|
45
|
+
const [previousQueryRef, setPreviousQueryRef] = React.useState(queryRef);
|
|
46
|
+
const [wrappedQueryRef, setWrappedQueryRef] = React.useState(queryRef);
|
|
47
|
+
const internalQueryRef = unwrapQueryRef(queryRef);
|
|
48
|
+
// To ensure we can support React transitions, this hook needs to manage the
|
|
49
|
+
// queryRef state and apply React's state value immediately to the existing
|
|
50
|
+
// queryRef since this hook doesn't return the queryRef directly
|
|
51
|
+
if (previousQueryRef !== queryRef) {
|
|
52
|
+
setPreviousQueryRef(queryRef);
|
|
53
|
+
setWrappedQueryRef(queryRef);
|
|
54
|
+
} else {
|
|
55
|
+
updateWrappedQueryRef(queryRef, getWrappedPromise(wrappedQueryRef));
|
|
56
|
+
}
|
|
57
|
+
const refetch = React.useCallback(
|
|
58
|
+
(variables) => {
|
|
59
|
+
const promise = internalQueryRef.refetch(variables);
|
|
60
|
+
setWrappedQueryRef(wrapQueryRef(internalQueryRef));
|
|
61
|
+
return promise;
|
|
62
|
+
},
|
|
63
|
+
[internalQueryRef],
|
|
64
|
+
);
|
|
65
|
+
const fetchMore = React.useCallback(
|
|
66
|
+
(options) => {
|
|
67
|
+
const promise = internalQueryRef.fetchMore(options);
|
|
68
|
+
setWrappedQueryRef(wrapQueryRef(internalQueryRef));
|
|
69
|
+
return promise;
|
|
70
|
+
},
|
|
71
|
+
[internalQueryRef],
|
|
72
|
+
);
|
|
73
|
+
return {
|
|
74
|
+
refetch,
|
|
75
|
+
fetchMore,
|
|
76
|
+
// TODO: The internalQueryRef doesn't have TVariables' type information so we have to cast it here
|
|
77
|
+
subscribeToMore: internalQueryRef.observable.subscribeToMore,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactiveVar } from '@apollo/client';
|
|
2
|
+
/**
|
|
3
|
+
* Reads the value of a [reactive variable](https://www.apollographql.com/docs/react/local-state/reactive-variables/) and re-renders the containing component whenever that variable's value changes. This enables a reactive variable to trigger changes _without_ relying on the `useQuery` hook.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
*
|
|
7
|
+
* ```jsx
|
|
8
|
+
* import { makeVar } from "@apollo/client";
|
|
9
|
+
* import { useReactiveVar } from "@octanejs/apollo-client/react";
|
|
10
|
+
* export const cartItemsVar = makeVar([]);
|
|
11
|
+
*
|
|
12
|
+
* export function Cart() {
|
|
13
|
+
* const cartItems = useReactiveVar(cartItemsVar);
|
|
14
|
+
* // ...
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @param rv - A reactive variable.
|
|
19
|
+
* @returns The current value of the reactive variable.
|
|
20
|
+
*/
|
|
21
|
+
export declare function useReactiveVar<T>(rv: ReactiveVar<T>): T;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from 'octane';
|
|
2
|
+
import { useSyncExternalStore } from './useSyncExternalStore.js';
|
|
3
|
+
/**
|
|
4
|
+
* Reads the value of a [reactive variable](https://www.apollographql.com/docs/react/local-state/reactive-variables/) and re-renders the containing component whenever that variable's value changes. This enables a reactive variable to trigger changes _without_ relying on the `useQuery` hook.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
*
|
|
8
|
+
* ```jsx
|
|
9
|
+
* import { makeVar } from "@apollo/client";
|
|
10
|
+
* import { useReactiveVar } from "@octanejs/apollo-client/react";
|
|
11
|
+
* export const cartItemsVar = makeVar([]);
|
|
12
|
+
*
|
|
13
|
+
* export function Cart() {
|
|
14
|
+
* const cartItems = useReactiveVar(cartItemsVar);
|
|
15
|
+
* // ...
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @param rv - A reactive variable.
|
|
20
|
+
* @returns The current value of the reactive variable.
|
|
21
|
+
*/
|
|
22
|
+
export function useReactiveVar(rv) {
|
|
23
|
+
return useSyncExternalStore(
|
|
24
|
+
React.useCallback(
|
|
25
|
+
(update) => {
|
|
26
|
+
// By reusing the same onNext function in the nested call to
|
|
27
|
+
// rv.onNextChange(onNext), we can keep using the initial clean-up function
|
|
28
|
+
// returned by rv.onNextChange(function onNext(v){...}), without having to
|
|
29
|
+
// register the new clean-up function (returned by the nested
|
|
30
|
+
// rv.onNextChange(onNext)) with yet another callback.
|
|
31
|
+
return rv.onNextChange(function onNext() {
|
|
32
|
+
update();
|
|
33
|
+
rv.onNextChange(onNext);
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
[rv],
|
|
37
|
+
),
|
|
38
|
+
rv,
|
|
39
|
+
rv,
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { DataState, ErrorLike, GetDataState, NetworkStatus } from '@apollo/client';
|
|
2
|
+
import type { MaybeMasked } from '@apollo/client/masking';
|
|
3
|
+
import type { QueryRef } from '@octanejs/apollo-client/react';
|
|
4
|
+
import type { DocumentationTypes as UtilityDocumentationTypes } from '@apollo/client/utilities/internal';
|
|
5
|
+
export declare namespace useReadQuery {
|
|
6
|
+
namespace Base {
|
|
7
|
+
interface Result<TData = unknown> {
|
|
8
|
+
/**
|
|
9
|
+
* A single ErrorLike object describing the error that occurred during the latest
|
|
10
|
+
* query execution.
|
|
11
|
+
*
|
|
12
|
+
* For more information, see [Handling operation errors](https://www.apollographql.com/docs/react/data/error-handling/).
|
|
13
|
+
*
|
|
14
|
+
* @docGroup 1. Operation data
|
|
15
|
+
*
|
|
16
|
+
*
|
|
17
|
+
* This property can be ignored when using the default `errorPolicy` or an
|
|
18
|
+
* `errorPolicy` of `none`. The hook will throw the error instead of setting
|
|
19
|
+
* this property.
|
|
20
|
+
*/
|
|
21
|
+
error: ErrorLike | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* A number indicating the current network state of the query's associated request. [See possible values.](https://github.com/apollographql/apollo-client/blob/d96f4578f89b933c281bb775a39503f6cdb59ee8/src/core/networkStatus.ts#L4)
|
|
24
|
+
*
|
|
25
|
+
* Used in conjunction with the [`notifyOnNetworkStatusChange`](#notifyonnetworkstatuschange) option.
|
|
26
|
+
*
|
|
27
|
+
* @docGroup 2. Network info
|
|
28
|
+
*/
|
|
29
|
+
networkStatus: NetworkStatus;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
type Result<
|
|
33
|
+
TData = unknown,
|
|
34
|
+
TStates extends DataState<TData>['dataState'] = DataState<TData>['dataState'],
|
|
35
|
+
> = Base.Result<TData> & GetDataState<MaybeMasked<TData>, TStates>;
|
|
36
|
+
namespace DocumentationTypes {
|
|
37
|
+
namespace useReadQuery {
|
|
38
|
+
interface Result<TData = unknown>
|
|
39
|
+
extends Base.Result<TData>, UtilityDocumentationTypes.DataState<TData> {}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* For a detailed explanation of `useReadQuery`, see the [fetching with Suspense reference](https://www.apollographql.com/docs/react/data/suspense#avoiding-request-waterfalls).
|
|
43
|
+
*
|
|
44
|
+
* @param queryRef - The `QueryRef` that was generated via `useBackgroundQuery`.
|
|
45
|
+
* @returns An object containing the query result data, error, and network status.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
*
|
|
49
|
+
* ```jsx
|
|
50
|
+
* import { Suspense } from "octane";
|
|
51
|
+
* import { useBackgroundQuery, useReadQuery } from "@apollo/client";
|
|
52
|
+
*
|
|
53
|
+
* function Parent() {
|
|
54
|
+
* const [queryRef] = useBackgroundQuery(query);
|
|
55
|
+
*
|
|
56
|
+
* return (
|
|
57
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
58
|
+
* <Child queryRef={queryRef} />
|
|
59
|
+
* </Suspense>
|
|
60
|
+
* );
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* function Child({ queryRef }) {
|
|
64
|
+
* const { data } = useReadQuery(queryRef);
|
|
65
|
+
*
|
|
66
|
+
* return <div>{data.name}</div>;
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
function useReadQuery<TData>(queryRef: QueryRef<TData>): useReadQuery.Result<TData>;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* For a detailed explanation of `useReadQuery`, see the [fetching with Suspense reference](https://www.apollographql.com/docs/react/data/suspense#avoiding-request-waterfalls).
|
|
75
|
+
*
|
|
76
|
+
* @param queryRef - The `QueryRef` that was generated via `useBackgroundQuery`.
|
|
77
|
+
* @returns An object containing the query result data, error, and network status.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
*
|
|
81
|
+
* ```jsx
|
|
82
|
+
* import { Suspense } from "octane";
|
|
83
|
+
* import { useBackgroundQuery, useReadQuery } from "@apollo/client";
|
|
84
|
+
*
|
|
85
|
+
* function Parent() {
|
|
86
|
+
* const [queryRef] = useBackgroundQuery(query);
|
|
87
|
+
*
|
|
88
|
+
* return (
|
|
89
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
90
|
+
* <Child queryRef={queryRef} />
|
|
91
|
+
* </Suspense>
|
|
92
|
+
* );
|
|
93
|
+
* }
|
|
94
|
+
*
|
|
95
|
+
* function Child({ queryRef }) {
|
|
96
|
+
* const { data } = useReadQuery(queryRef);
|
|
97
|
+
*
|
|
98
|
+
* return <div>{data.name}</div>;
|
|
99
|
+
* }
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export declare function useReadQuery<TData, TStates extends DataState<TData>['dataState']>(
|
|
103
|
+
queryRef: QueryRef<TData, any, TStates>,
|
|
104
|
+
): useReadQuery.Result<TData, TStates>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import * as React from 'octane';
|
|
2
|
+
import {
|
|
3
|
+
assertWrappedQueryRef,
|
|
4
|
+
getWrappedPromise,
|
|
5
|
+
unwrapQueryRef,
|
|
6
|
+
updateWrappedQueryRef,
|
|
7
|
+
} from '@octanejs/apollo-client/react/internal';
|
|
8
|
+
import { __use, wrapHook } from './internal/index.js';
|
|
9
|
+
import { useApolloClient } from './useApolloClient.js';
|
|
10
|
+
import { useSyncExternalStore } from './useSyncExternalStore.js';
|
|
11
|
+
/**
|
|
12
|
+
* For a detailed explanation of `useReadQuery`, see the [fetching with Suspense reference](https://www.apollographql.com/docs/react/data/suspense#avoiding-request-waterfalls).
|
|
13
|
+
*
|
|
14
|
+
* @param queryRef - The `QueryRef` that was generated via `useBackgroundQuery`.
|
|
15
|
+
* @returns An object containing the query result data, error, and network status.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
*
|
|
19
|
+
* ```jsx
|
|
20
|
+
* import { Suspense } from "octane";
|
|
21
|
+
* import { useBackgroundQuery, useReadQuery } from "@apollo/client";
|
|
22
|
+
*
|
|
23
|
+
* function Parent() {
|
|
24
|
+
* const [queryRef] = useBackgroundQuery(query);
|
|
25
|
+
*
|
|
26
|
+
* return (
|
|
27
|
+
* <Suspense fallback={<div>Loading...</div>}>
|
|
28
|
+
* <Child queryRef={queryRef} />
|
|
29
|
+
* </Suspense>
|
|
30
|
+
* );
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* function Child({ queryRef }) {
|
|
34
|
+
* const { data } = useReadQuery(queryRef);
|
|
35
|
+
*
|
|
36
|
+
* return <div>{data.name}</div>;
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export function useReadQuery(queryRef, site) {
|
|
41
|
+
'use no memo';
|
|
42
|
+
const unwrapped = unwrapQueryRef(queryRef);
|
|
43
|
+
const clientOrObsQuery = useApolloClient(
|
|
44
|
+
unwrapped
|
|
45
|
+
? // passing an `ObservableQuery` is not supported by the types, but it will
|
|
46
|
+
// return any truthy value that is passed in as an override so we cast the result
|
|
47
|
+
unwrapped['observable']
|
|
48
|
+
: undefined,
|
|
49
|
+
);
|
|
50
|
+
return wrapHook('useReadQuery', useReadQuery_, clientOrObsQuery)(queryRef, site);
|
|
51
|
+
}
|
|
52
|
+
function useReadQuery_(queryRef, site) {
|
|
53
|
+
assertWrappedQueryRef(queryRef);
|
|
54
|
+
const internalQueryRef = React.useMemo(() => unwrapQueryRef(queryRef), [queryRef]);
|
|
55
|
+
const getPromise = React.useCallback(() => getWrappedPromise(queryRef), [queryRef]);
|
|
56
|
+
if (internalQueryRef.disposed) {
|
|
57
|
+
internalQueryRef.reinitialize();
|
|
58
|
+
updateWrappedQueryRef(queryRef, internalQueryRef.promise);
|
|
59
|
+
}
|
|
60
|
+
React.useEffect(() => internalQueryRef.retain(), [internalQueryRef]);
|
|
61
|
+
const promise = useSyncExternalStore(
|
|
62
|
+
React.useCallback(
|
|
63
|
+
(forceUpdate) => {
|
|
64
|
+
return internalQueryRef.listen((promise) => {
|
|
65
|
+
updateWrappedQueryRef(queryRef, promise);
|
|
66
|
+
forceUpdate();
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
[internalQueryRef, queryRef],
|
|
70
|
+
),
|
|
71
|
+
getPromise,
|
|
72
|
+
getPromise,
|
|
73
|
+
);
|
|
74
|
+
const result = __use(promise, site);
|
|
75
|
+
return React.useMemo(() => {
|
|
76
|
+
return {
|
|
77
|
+
data: result.data,
|
|
78
|
+
dataState: result.dataState,
|
|
79
|
+
networkStatus: result.networkStatus,
|
|
80
|
+
error: result.error,
|
|
81
|
+
};
|
|
82
|
+
}, [result]);
|
|
83
|
+
}
|