@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.
Files changed (85) hide show
  1. package/LICENSE +49 -0
  2. package/README.md +97 -0
  3. package/package.json +77 -0
  4. package/src/index.d.ts +1 -0
  5. package/src/index.js +3 -0
  6. package/src/react/context/ApolloContext.d.ts +8 -0
  7. package/src/react/context/ApolloContext.js +21 -0
  8. package/src/react/context/ApolloProvider.d.ts +9 -0
  9. package/src/react/context/ApolloProvider.js +15 -0
  10. package/src/react/hooks/constants.d.ts +2 -0
  11. package/src/react/hooks/constants.js +1 -0
  12. package/src/react/hooks/internal/__use.d.ts +3 -0
  13. package/src/react/hooks/internal/__use.js +9 -0
  14. package/src/react/hooks/internal/index.d.ts +5 -0
  15. package/src/react/hooks/internal/index.js +6 -0
  16. package/src/react/hooks/internal/site.d.ts +2 -0
  17. package/src/react/hooks/internal/site.js +20 -0
  18. package/src/react/hooks/internal/useDeepMemo.d.ts +5 -0
  19. package/src/react/hooks/internal/useDeepMemo.js +11 -0
  20. package/src/react/hooks/internal/useIsomorphicLayoutEffect.d.ts +2 -0
  21. package/src/react/hooks/internal/useIsomorphicLayoutEffect.js +8 -0
  22. package/src/react/hooks/internal/useRenderGuard.d.ts +1 -0
  23. package/src/react/hooks/internal/useRenderGuard.js +14 -0
  24. package/src/react/hooks/internal/useSuspenseHookCacheKey.d.ts +13 -0
  25. package/src/react/hooks/internal/useSuspenseHookCacheKey.js +19 -0
  26. package/src/react/hooks/internal/validateSuspenseHookOptions.d.ts +4 -0
  27. package/src/react/hooks/internal/validateSuspenseHookOptions.js +16 -0
  28. package/src/react/hooks/internal/wrapHook.d.ts +77 -0
  29. package/src/react/hooks/internal/wrapHook.js +64 -0
  30. package/src/react/hooks/useApolloClient.d.ts +17 -0
  31. package/src/react/hooks/useApolloClient.js +27 -0
  32. package/src/react/hooks/useBackgroundQuery.d.ts +3061 -0
  33. package/src/react/hooks/useBackgroundQuery.js +88 -0
  34. package/src/react/hooks/useFragment.d.ts +205 -0
  35. package/src/react/hooks/useFragment.js +75 -0
  36. package/src/react/hooks/useLazyQuery.d.ts +947 -0
  37. package/src/react/hooks/useLazyQuery.js +156 -0
  38. package/src/react/hooks/useLoadableQuery.d.ts +955 -0
  39. package/src/react/hooks/useLoadableQuery.js +98 -0
  40. package/src/react/hooks/useMutation.d.ts +585 -0
  41. package/src/react/hooks/useMutation.js +121 -0
  42. package/src/react/hooks/useQuery.d.ts +1401 -0
  43. package/src/react/hooks/useQuery.js +234 -0
  44. package/src/react/hooks/useQueryRefHandlers.d.ts +85 -0
  45. package/src/react/hooks/useQueryRefHandlers.js +79 -0
  46. package/src/react/hooks/useReactiveVar.d.ts +21 -0
  47. package/src/react/hooks/useReactiveVar.js +41 -0
  48. package/src/react/hooks/useReadQuery.d.ts +104 -0
  49. package/src/react/hooks/useReadQuery.js +83 -0
  50. package/src/react/hooks/useSubscription.d.ts +289 -0
  51. package/src/react/hooks/useSubscription.js +258 -0
  52. package/src/react/hooks/useSuspenseFragment.d.ts +154 -0
  53. package/src/react/hooks/useSuspenseFragment.js +50 -0
  54. package/src/react/hooks/useSuspenseQuery.d.ts +1629 -0
  55. package/src/react/hooks/useSuspenseQuery.js +130 -0
  56. package/src/react/hooks/useSyncExternalStore.d.ts +2 -0
  57. package/src/react/hooks/useSyncExternalStore.js +125 -0
  58. package/src/react/index.d.ts +68 -0
  59. package/src/react/index.js +18 -0
  60. package/src/react/internal/cache/FragmentReference.d.ts +41 -0
  61. package/src/react/internal/cache/FragmentReference.js +117 -0
  62. package/src/react/internal/cache/QueryReference.d.ts +180 -0
  63. package/src/react/internal/cache/QueryReference.js +310 -0
  64. package/src/react/internal/cache/SuspenseCache.d.ts +38 -0
  65. package/src/react/internal/cache/SuspenseCache.js +39 -0
  66. package/src/react/internal/cache/getSuspenseCache.d.ts +26 -0
  67. package/src/react/internal/cache/getSuspenseCache.js +8 -0
  68. package/src/react/internal/cache/types.d.ts +13 -0
  69. package/src/react/internal/cache/types.js +1 -0
  70. package/src/react/internal/index.d.ts +16 -0
  71. package/src/react/internal/index.js +10 -0
  72. package/src/react/internal/types.d.ts +15 -0
  73. package/src/react/internal/types.js +1 -0
  74. package/src/react/query-preloader/createQueryPreloader.d.ts +630 -0
  75. package/src/react/query-preloader/createQueryPreloader.js +89 -0
  76. package/src/react/types/deprecated.d.ts +140 -0
  77. package/src/react/types/deprecated.js +1 -0
  78. package/src/react/types/types.documentation.d.ts +551 -0
  79. package/src/react/types/types.documentation.js +1 -0
  80. package/src/testing/index.d.ts +1 -0
  81. package/src/testing/index.js +1 -0
  82. package/src/testing/react/MockedProvider.d.ts +21 -0
  83. package/src/testing/react/MockedProvider.js +53 -0
  84. package/src/testing/react/index.d.ts +2 -0
  85. 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
+ }