@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,88 @@
|
|
|
1
|
+
import * as React from 'octane';
|
|
2
|
+
import {
|
|
3
|
+
getSuspenseCache,
|
|
4
|
+
unwrapQueryRef,
|
|
5
|
+
updateWrappedQueryRef,
|
|
6
|
+
wrapQueryRef,
|
|
7
|
+
} from '@octanejs/apollo-client/react/internal';
|
|
8
|
+
import { skipToken } from './constants.js';
|
|
9
|
+
import { useSuspenseHookCacheKey, wrapHook } from './internal/index.js';
|
|
10
|
+
import { isCompilerSite } from './internal/site.js';
|
|
11
|
+
import { useApolloClient } from './useApolloClient.js';
|
|
12
|
+
import { useWatchQueryOptions } from './useSuspenseQuery.js';
|
|
13
|
+
export const useBackgroundQuery = function useBackgroundQuery(query, options) {
|
|
14
|
+
'use no memo';
|
|
15
|
+
if (isCompilerSite(options, skipToken)) options = undefined;
|
|
16
|
+
return wrapHook(
|
|
17
|
+
'useBackgroundQuery',
|
|
18
|
+
useBackgroundQuery_,
|
|
19
|
+
useApolloClient(typeof options === 'object' ? options.client : undefined),
|
|
20
|
+
)(query, options ?? {});
|
|
21
|
+
};
|
|
22
|
+
function useBackgroundQuery_(query, options) {
|
|
23
|
+
const client = useApolloClient(options.client);
|
|
24
|
+
const suspenseCache = getSuspenseCache(client);
|
|
25
|
+
const watchQueryOptions = useWatchQueryOptions({ client, query, options });
|
|
26
|
+
const { fetchPolicy } = watchQueryOptions;
|
|
27
|
+
const cacheKey = useSuspenseHookCacheKey(query, options);
|
|
28
|
+
// This ref tracks the first time query execution is enabled to determine
|
|
29
|
+
// whether to return a query ref or `undefined`. When initialized
|
|
30
|
+
// in a skipped state (either via `skip: true` or `skipToken`) we return
|
|
31
|
+
// `undefined` for the `queryRef` until the query has been enabled. Once
|
|
32
|
+
// enabled, a query ref is always returned regardless of whether the query is
|
|
33
|
+
// skipped again later.
|
|
34
|
+
const didFetchResult = React.useRef(fetchPolicy !== 'standby');
|
|
35
|
+
didFetchResult.current ||= fetchPolicy !== 'standby';
|
|
36
|
+
const queryRef = suspenseCache.getQueryRef(cacheKey, () => client.watchQuery(watchQueryOptions));
|
|
37
|
+
const [wrappedQueryRef, setWrappedQueryRef] = React.useState(wrapQueryRef(queryRef));
|
|
38
|
+
if (unwrapQueryRef(wrappedQueryRef) !== queryRef) {
|
|
39
|
+
setWrappedQueryRef(wrapQueryRef(queryRef));
|
|
40
|
+
}
|
|
41
|
+
if (queryRef.didChangeOptions(watchQueryOptions)) {
|
|
42
|
+
const promise = queryRef.applyOptions(watchQueryOptions);
|
|
43
|
+
updateWrappedQueryRef(wrappedQueryRef, promise);
|
|
44
|
+
}
|
|
45
|
+
// This prevents issues where rerendering useBackgroundQuery after the
|
|
46
|
+
// queryRef has been disposed would cause the hook to return a new queryRef
|
|
47
|
+
// instance since disposal also removes it from the suspense cache. We add
|
|
48
|
+
// the queryRef back in the suspense cache so that the next render will reuse
|
|
49
|
+
// this queryRef rather than initializing a new instance.
|
|
50
|
+
React.useEffect(() => {
|
|
51
|
+
// Since the queryRef is disposed async via `setTimeout`, we have to wait a
|
|
52
|
+
// tick before checking it and adding back to the suspense cache.
|
|
53
|
+
const id = setTimeout(() => {
|
|
54
|
+
if (queryRef.disposed) {
|
|
55
|
+
suspenseCache.add(cacheKey, queryRef);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return () => clearTimeout(id);
|
|
59
|
+
// Omitting the deps is intentional. This avoids stale closures and the
|
|
60
|
+
// conditional ensures we aren't running the logic on each render.
|
|
61
|
+
}, null);
|
|
62
|
+
const fetchMore = React.useCallback(
|
|
63
|
+
(options) => {
|
|
64
|
+
const promise = queryRef.fetchMore(options);
|
|
65
|
+
setWrappedQueryRef(wrapQueryRef(queryRef));
|
|
66
|
+
return promise;
|
|
67
|
+
},
|
|
68
|
+
[queryRef],
|
|
69
|
+
);
|
|
70
|
+
const refetch = React.useCallback(
|
|
71
|
+
(variables) => {
|
|
72
|
+
const promise = queryRef.refetch(variables);
|
|
73
|
+
setWrappedQueryRef(wrapQueryRef(queryRef));
|
|
74
|
+
return promise;
|
|
75
|
+
},
|
|
76
|
+
[queryRef],
|
|
77
|
+
);
|
|
78
|
+
React.useEffect(() => queryRef.softRetain(), [queryRef]);
|
|
79
|
+
return [
|
|
80
|
+
didFetchResult.current ? wrappedQueryRef : void 0,
|
|
81
|
+
{
|
|
82
|
+
fetchMore,
|
|
83
|
+
refetch,
|
|
84
|
+
// TODO: The internalQueryRef doesn't have TVariables' type information so we have to cast it here
|
|
85
|
+
subscribeToMore: queryRef.observable.subscribeToMore,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ApolloClient,
|
|
3
|
+
DataValue,
|
|
4
|
+
DocumentNode,
|
|
5
|
+
GetDataState,
|
|
6
|
+
OperationVariables,
|
|
7
|
+
TypedDocumentNode,
|
|
8
|
+
} from '@apollo/client';
|
|
9
|
+
import type { ApolloCache, MissingTree } from '@apollo/client/cache';
|
|
10
|
+
import type { MaybeMasked } from '@apollo/client/masking';
|
|
11
|
+
import type { NoInfer } from '@apollo/client/utilities/internal';
|
|
12
|
+
export declare namespace useFragment {
|
|
13
|
+
import _self = useFragment;
|
|
14
|
+
interface Options<TData, TVariables extends OperationVariables> {
|
|
15
|
+
/**
|
|
16
|
+
* A GraphQL document created using the `gql` template string tag from
|
|
17
|
+
* `graphql-tag` with one or more fragments which will be used to determine
|
|
18
|
+
* the shape of data to read. If you provide more than one fragment in this
|
|
19
|
+
* document then you must also specify `fragmentName` to select a single.
|
|
20
|
+
*/
|
|
21
|
+
fragment: DocumentNode | TypedDocumentNode<TData, TVariables>;
|
|
22
|
+
/**
|
|
23
|
+
* The name of the fragment in your GraphQL document to be used. If you do
|
|
24
|
+
* not provide a `fragmentName` and there is only one fragment in your
|
|
25
|
+
* `fragment` document then that fragment will be used.
|
|
26
|
+
*/
|
|
27
|
+
fragmentName?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Any variables that the GraphQL query may depend on.
|
|
30
|
+
*/
|
|
31
|
+
variables?: NoInfer<TVariables>;
|
|
32
|
+
/**
|
|
33
|
+
* An object or array containing a `__typename` and primary key fields
|
|
34
|
+
* (such as `id`) identifying the entity object from which the fragment will
|
|
35
|
+
* be retrieved, or a `{ __ref: "..." }` reference, or a `string` ID (uncommon).
|
|
36
|
+
*/
|
|
37
|
+
from:
|
|
38
|
+
| useFragment.FromOptionValue<TData>
|
|
39
|
+
| Array<useFragment.FromOptionValue<TData> | null>
|
|
40
|
+
| null;
|
|
41
|
+
/**
|
|
42
|
+
* Whether to read from optimistic or non-optimistic cache data. If
|
|
43
|
+
* this named option is provided, the optimistic parameter of the
|
|
44
|
+
* readQuery method can be omitted.
|
|
45
|
+
*
|
|
46
|
+
* @defaultValue true
|
|
47
|
+
*/
|
|
48
|
+
optimistic?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* The instance of `ApolloClient` to use to look up the fragment.
|
|
51
|
+
*
|
|
52
|
+
* By default, the instance that's passed down via context is used, but you
|
|
53
|
+
* can provide a different instance here.
|
|
54
|
+
*
|
|
55
|
+
* @docGroup 1. Operation options
|
|
56
|
+
*/
|
|
57
|
+
client?: ApolloClient;
|
|
58
|
+
}
|
|
59
|
+
namespace DocumentationTypes {
|
|
60
|
+
namespace useFragment {
|
|
61
|
+
interface Options<
|
|
62
|
+
TData = unknown,
|
|
63
|
+
TVariables extends OperationVariables = OperationVariables,
|
|
64
|
+
> extends _self.Options<TData, TVariables> {}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Acceptable values provided to the `from` option.
|
|
69
|
+
*/
|
|
70
|
+
type FromOptionValue<TData> = ApolloCache.FromOptionValue<TData>;
|
|
71
|
+
type Result<TData> =
|
|
72
|
+
| ({
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
*/
|
|
76
|
+
complete: true;
|
|
77
|
+
/**
|
|
78
|
+
* A tree of all `MissingFieldError` messages reported during fragment reading, where the branches of the tree indicate the paths of the errors within the query result.
|
|
79
|
+
*/
|
|
80
|
+
missing?: never;
|
|
81
|
+
} & GetDataState<MaybeMasked<TData>, 'complete'>)
|
|
82
|
+
| {
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
*/
|
|
86
|
+
complete: false;
|
|
87
|
+
/**
|
|
88
|
+
* A tree of all `MissingFieldError` messages reported during fragment reading, where the branches of the tree indicate the paths of the errors within the query result.
|
|
89
|
+
*/
|
|
90
|
+
missing?: MissingTree;
|
|
91
|
+
/**
|
|
92
|
+
* An object containing the result of your GraphQL query after it completes.
|
|
93
|
+
*
|
|
94
|
+
* This value might be `undefined` if a query results in one or more errors (depending on the query's `errorPolicy`).
|
|
95
|
+
*
|
|
96
|
+
* @docGroup 1. Operation data
|
|
97
|
+
*/
|
|
98
|
+
data: TData extends Array<infer TItem>
|
|
99
|
+
? Array<DataValue.Partial<TItem> | null>
|
|
100
|
+
: DataValue.Partial<TData>;
|
|
101
|
+
/**
|
|
102
|
+
* Describes the completeness of `data`.
|
|
103
|
+
*
|
|
104
|
+
* - `empty`: No data could be fulfilled from the cache or the result is
|
|
105
|
+
* incomplete. `data` is `undefined`.
|
|
106
|
+
* - `partial`: Some data could be fulfilled from the cache but `data` is
|
|
107
|
+
* incomplete. This is only possible when `returnPartialData` is `true`.
|
|
108
|
+
* - `streaming`: `data` is incomplete as a result of a deferred query and
|
|
109
|
+
* the result is still streaming in.
|
|
110
|
+
* - `complete`: `data` is a fully satisfied query result fulfilled
|
|
111
|
+
* either from the cache or network.
|
|
112
|
+
*
|
|
113
|
+
* @docGroup 1. Operation data
|
|
114
|
+
*/
|
|
115
|
+
dataState: 'partial';
|
|
116
|
+
};
|
|
117
|
+
namespace DocumentationTypes {
|
|
118
|
+
namespace useFragment {
|
|
119
|
+
interface Result<TData> {
|
|
120
|
+
data: MaybeMasked<TData> | DataValue.Partial<MaybeMasked<TData>>;
|
|
121
|
+
complete: boolean;
|
|
122
|
+
/**
|
|
123
|
+
* A tree of all `MissingFieldError` messages reported during fragment reading, where the branches of the tree indicate the paths of the errors within the query result.
|
|
124
|
+
*/
|
|
125
|
+
missing?: MissingTree;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
namespace DocumentationTypes {
|
|
130
|
+
/**
|
|
131
|
+
* `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
|
|
132
|
+
*
|
|
133
|
+
* Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
|
|
134
|
+
*
|
|
135
|
+
* To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
|
|
136
|
+
*/
|
|
137
|
+
function useFragment<
|
|
138
|
+
TData = unknown,
|
|
139
|
+
TVariables extends OperationVariables = OperationVariables,
|
|
140
|
+
>({
|
|
141
|
+
fragment,
|
|
142
|
+
from,
|
|
143
|
+
fragmentName,
|
|
144
|
+
variables,
|
|
145
|
+
optimistic,
|
|
146
|
+
client,
|
|
147
|
+
}: useFragment.Options<TData, TVariables>): useFragment.Result<TData>;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
|
|
152
|
+
*
|
|
153
|
+
* Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
|
|
154
|
+
*
|
|
155
|
+
* To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
|
|
156
|
+
*/
|
|
157
|
+
export declare function useFragment<
|
|
158
|
+
TData = unknown,
|
|
159
|
+
TVariables extends OperationVariables = OperationVariables,
|
|
160
|
+
>(
|
|
161
|
+
options: useFragment.Options<TData, TVariables> & {
|
|
162
|
+
from: Array<useFragment.FromOptionValue<TData>>;
|
|
163
|
+
},
|
|
164
|
+
): useFragment.Result<Array<TData>>;
|
|
165
|
+
/**
|
|
166
|
+
* `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
|
|
167
|
+
*
|
|
168
|
+
* Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
|
|
169
|
+
*
|
|
170
|
+
* To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
|
|
171
|
+
*/
|
|
172
|
+
export declare function useFragment<
|
|
173
|
+
TData = unknown,
|
|
174
|
+
TVariables extends OperationVariables = OperationVariables,
|
|
175
|
+
>(
|
|
176
|
+
options: useFragment.Options<TData, TVariables> & {
|
|
177
|
+
from: Array<null>;
|
|
178
|
+
},
|
|
179
|
+
): useFragment.Result<Array<null>>;
|
|
180
|
+
/**
|
|
181
|
+
* `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
|
|
182
|
+
*
|
|
183
|
+
* Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
|
|
184
|
+
*
|
|
185
|
+
* To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
|
|
186
|
+
*/
|
|
187
|
+
export declare function useFragment<
|
|
188
|
+
TData = unknown,
|
|
189
|
+
TVariables extends OperationVariables = OperationVariables,
|
|
190
|
+
>(
|
|
191
|
+
options: useFragment.Options<TData, TVariables> & {
|
|
192
|
+
from: Array<useFragment.FromOptionValue<TData> | null>;
|
|
193
|
+
},
|
|
194
|
+
): useFragment.Result<Array<TData | null>>;
|
|
195
|
+
/**
|
|
196
|
+
* `useFragment` represents a lightweight live binding into the Apollo Client Cache and enables Apollo Client to broadcast very specific fragment results to individual components. This hook returns an always-up-to-date view of whatever data the cache currently contains for a given fragment. `useFragment` never triggers network requests of its own.
|
|
197
|
+
*
|
|
198
|
+
* Note that the `useQuery` hook remains the primary hook responsible for querying and populating data in the cache ([see the API reference](./hooks#usequery)). As a result, the component reading the fragment data via `useFragment` is still subscribed to all changes in the query data, but receives updates only when that fragment's specific data change.
|
|
199
|
+
*
|
|
200
|
+
* To view a `useFragment` example, see the [Fragments](https://www.apollographql.com/docs/react/data/fragments#usefragment) page.
|
|
201
|
+
*/
|
|
202
|
+
export declare function useFragment<
|
|
203
|
+
TData = unknown,
|
|
204
|
+
TVariables extends OperationVariables = OperationVariables,
|
|
205
|
+
>(options: useFragment.Options<TData, TVariables>): useFragment.Result<TData>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as React from 'octane';
|
|
2
|
+
import { useDeepMemo, wrapHook } from './internal/index.js';
|
|
3
|
+
import { subSite } from './internal/site.js';
|
|
4
|
+
import { useApolloClient } from './useApolloClient.js';
|
|
5
|
+
import { useSyncExternalStore } from './useSyncExternalStore.js';
|
|
6
|
+
export function useFragment(options, site) {
|
|
7
|
+
'use no memo';
|
|
8
|
+
return wrapHook('useFragment', useFragment_, useApolloClient(options.client))(options, site);
|
|
9
|
+
}
|
|
10
|
+
function useFragment_(options, site) {
|
|
11
|
+
const client = useApolloClient(options.client);
|
|
12
|
+
const { from, ...rest } = options;
|
|
13
|
+
const { cache } = client;
|
|
14
|
+
// We calculate the cache id seperately because we don't want changes to non
|
|
15
|
+
// key fields in the `from` property to recreate the observable. If the cache
|
|
16
|
+
// identifier stays the same between renders, we want to reuse the existing
|
|
17
|
+
// subscription.
|
|
18
|
+
const ids = useDeepMemo(
|
|
19
|
+
() => {
|
|
20
|
+
const fromArray = Array.isArray(from) ? from : [from];
|
|
21
|
+
const ids = fromArray.map((value) =>
|
|
22
|
+
typeof value === 'string' ? value : value === null ? null : cache.identify(value),
|
|
23
|
+
);
|
|
24
|
+
return Array.isArray(from) ? ids : ids[0];
|
|
25
|
+
},
|
|
26
|
+
[cache, from],
|
|
27
|
+
subSite(site, 'ids'),
|
|
28
|
+
);
|
|
29
|
+
const stableOptions = useDeepMemo(
|
|
30
|
+
() => ({ ...rest, from: ids }),
|
|
31
|
+
[rest, ids],
|
|
32
|
+
subSite(site, 'options'),
|
|
33
|
+
);
|
|
34
|
+
const observable = React.useMemo(
|
|
35
|
+
() => client.watchFragment(stableOptions),
|
|
36
|
+
[client, stableOptions],
|
|
37
|
+
);
|
|
38
|
+
// Unfortunately we forgot to update the use case of `from: null` on
|
|
39
|
+
// useFragment in 4.0 to match `useSuspenseFragment`. As such, we need to
|
|
40
|
+
// fallback to data: {} with complete: false when `from` is `null` to maintain
|
|
41
|
+
// backwards compatibility. We should plan to change this in v5.
|
|
42
|
+
const getSnapshot = React.useCallback(
|
|
43
|
+
() => (from === null ? nullResult : observable.getCurrentResult()),
|
|
44
|
+
[from, observable],
|
|
45
|
+
);
|
|
46
|
+
return useSyncExternalStore(
|
|
47
|
+
React.useCallback(
|
|
48
|
+
(update) => {
|
|
49
|
+
let lastTimeout = 0;
|
|
50
|
+
const subscription = observable.subscribe({
|
|
51
|
+
next: () => {
|
|
52
|
+
// If we get another update before we've re-rendered, bail out of
|
|
53
|
+
// the update and try again. This ensures that the relative timing
|
|
54
|
+
// between useQuery and useFragment stays roughly the same as
|
|
55
|
+
// fixed in https://github.com/apollographql/apollo-client/pull/11083
|
|
56
|
+
clearTimeout(lastTimeout);
|
|
57
|
+
lastTimeout = setTimeout(update);
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
return () => {
|
|
61
|
+
subscription.unsubscribe();
|
|
62
|
+
clearTimeout(lastTimeout);
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
[observable],
|
|
66
|
+
),
|
|
67
|
+
getSnapshot,
|
|
68
|
+
getSnapshot,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const nullResult = Object.freeze({
|
|
72
|
+
data: {},
|
|
73
|
+
dataState: 'partial',
|
|
74
|
+
complete: false,
|
|
75
|
+
});
|