@isograph/react 0.2.0 → 0.3.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/dist/core/FragmentReference.d.ts +14 -4
- package/dist/core/FragmentReference.d.ts.map +1 -0
- package/dist/core/FragmentReference.js +2 -3
- package/dist/core/IsographEnvironment.d.ts +28 -10
- package/dist/core/IsographEnvironment.d.ts.map +1 -0
- package/dist/core/IsographEnvironment.js +15 -22
- package/dist/core/PromiseWrapper.d.ts +1 -0
- package/dist/core/PromiseWrapper.d.ts.map +1 -0
- package/dist/core/PromiseWrapper.js +4 -5
- package/dist/core/areEqualWithDeepComparison.d.ts +5 -3
- package/dist/core/areEqualWithDeepComparison.d.ts.map +1 -0
- package/dist/core/areEqualWithDeepComparison.js +73 -39
- package/dist/core/cache.d.ts +26 -10
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js +160 -98
- package/dist/core/check.d.ts +18 -0
- package/dist/core/check.d.ts.map +1 -0
- package/dist/core/check.js +127 -0
- package/dist/core/componentCache.d.ts +1 -1
- package/dist/core/componentCache.d.ts.map +1 -0
- package/dist/core/componentCache.js +14 -14
- package/dist/core/entrypoint.d.ts +27 -8
- package/dist/core/entrypoint.d.ts.map +1 -0
- package/dist/core/entrypoint.js +1 -2
- package/dist/core/garbageCollection.d.ts +3 -1
- package/dist/core/garbageCollection.d.ts.map +1 -0
- package/dist/core/garbageCollection.js +48 -15
- package/dist/core/logging.d.ts +69 -0
- package/dist/core/logging.d.ts.map +1 -0
- package/dist/core/logging.js +19 -0
- package/dist/core/makeNetworkRequest.d.ts +4 -1
- package/dist/core/makeNetworkRequest.d.ts.map +1 -0
- package/dist/core/makeNetworkRequest.js +71 -15
- package/dist/core/read.d.ts +20 -5
- package/dist/core/read.d.ts.map +1 -0
- package/dist/core/read.js +104 -41
- package/dist/core/reader.d.ts +34 -10
- package/dist/core/reader.d.ts.map +1 -0
- package/dist/core/util.d.ts +2 -0
- package/dist/core/util.d.ts.map +1 -0
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -2
- package/dist/loadable-hooks/useClientSideDefer.d.ts +15 -3
- package/dist/loadable-hooks/useClientSideDefer.d.ts.map +1 -0
- package/dist/loadable-hooks/useClientSideDefer.js +4 -6
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts +34 -0
- package/dist/loadable-hooks/useConnectionSpecPagination.d.ts.map +1 -0
- package/dist/loadable-hooks/useConnectionSpecPagination.js +160 -0
- package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts +1 -0
- package/dist/loadable-hooks/useImperativeExposedMutationField.d.ts.map +1 -0
- package/dist/loadable-hooks/useImperativeExposedMutationField.js +1 -2
- package/dist/loadable-hooks/useImperativeLoadableField.d.ts +13 -5
- package/dist/loadable-hooks/useImperativeLoadableField.d.ts.map +1 -0
- package/dist/loadable-hooks/useImperativeLoadableField.js +3 -4
- package/dist/loadable-hooks/useSkipLimitPagination.d.ts +18 -24
- package/dist/loadable-hooks/useSkipLimitPagination.d.ts.map +1 -0
- package/dist/loadable-hooks/useSkipLimitPagination.js +88 -44
- package/dist/react/FragmentReader.d.ts +7 -4
- package/dist/react/FragmentReader.d.ts.map +1 -0
- package/dist/react/FragmentReader.js +4 -2
- package/dist/react/IsographEnvironmentProvider.d.ts +1 -0
- package/dist/react/IsographEnvironmentProvider.d.ts.map +1 -0
- package/dist/react/IsographEnvironmentProvider.js +3 -3
- package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts +10 -0
- package/dist/react/RenderAfterCommit__DO_NOT_USE.d.ts.map +1 -0
- package/dist/react/RenderAfterCommit__DO_NOT_USE.js +15 -0
- package/dist/react/useImperativeReference.d.ts +8 -3
- package/dist/react/useImperativeReference.d.ts.map +1 -0
- package/dist/react/useImperativeReference.js +4 -5
- package/dist/react/useLazyReference.d.ts +7 -2
- package/dist/react/useLazyReference.d.ts.map +1 -0
- package/dist/react/useLazyReference.js +11 -4
- package/dist/react/useReadAndSubscribe.d.ts +12 -3
- package/dist/react/useReadAndSubscribe.d.ts.map +1 -0
- package/dist/react/useReadAndSubscribe.js +6 -7
- package/dist/react/useRerenderOnChange.d.ts +6 -1
- package/dist/react/useRerenderOnChange.d.ts.map +1 -0
- package/dist/react/useRerenderOnChange.js +3 -4
- package/dist/react/useResult.d.ts +5 -1
- package/dist/react/useResult.d.ts.map +1 -0
- package/dist/react/useResult.js +8 -5
- package/{src/tests/isograph.config.json → isograph.config.json} +1 -1
- package/package.json +12 -8
- package/{src/tests/schema.graphql → schema.graphql} +1 -0
- package/src/core/FragmentReference.ts +17 -5
- package/src/core/IsographEnvironment.ts +38 -29
- package/src/core/areEqualWithDeepComparison.ts +76 -42
- package/src/core/cache.ts +237 -123
- package/src/core/check.ts +207 -0
- package/src/core/componentCache.ts +18 -17
- package/src/core/entrypoint.ts +15 -8
- package/src/core/garbageCollection.ts +71 -20
- package/src/core/logging.ts +116 -0
- package/src/core/makeNetworkRequest.ts +89 -13
- package/src/core/read.ts +162 -55
- package/src/core/reader.ts +40 -13
- package/src/core/util.ts +4 -0
- package/src/index.ts +14 -1
- package/src/loadable-hooks/useClientSideDefer.ts +45 -15
- package/src/loadable-hooks/useConnectionSpecPagination.ts +331 -0
- package/src/loadable-hooks/useImperativeLoadableField.ts +36 -10
- package/src/loadable-hooks/useSkipLimitPagination.ts +231 -90
- package/src/react/FragmentReader.tsx +13 -4
- package/src/react/RenderAfterCommit__DO_NOT_USE.tsx +17 -0
- package/src/react/useImperativeReference.ts +18 -7
- package/src/react/useLazyReference.ts +24 -4
- package/src/react/useReadAndSubscribe.ts +20 -5
- package/src/react/useRerenderOnChange.ts +6 -1
- package/src/react/useResult.ts +10 -2
- package/src/tests/__isograph/Query/meName/entrypoint.ts +7 -2
- package/src/tests/__isograph/Query/meName/param_type.ts +5 -2
- package/src/tests/__isograph/Query/meName/resolver_reader.ts +1 -0
- package/src/tests/__isograph/Query/meNameSuccessor/entrypoint.ts +9 -2
- package/src/tests/__isograph/Query/meNameSuccessor/param_type.ts +9 -6
- package/src/tests/__isograph/Query/meNameSuccessor/resolver_reader.ts +3 -0
- package/src/tests/__isograph/Query/nodeField/entrypoint.ts +13 -2
- package/src/tests/__isograph/Query/nodeField/param_type.ts +7 -3
- package/src/tests/__isograph/Query/nodeField/parameters_type.ts +3 -0
- package/src/tests/__isograph/Query/nodeField/resolver_reader.ts +1 -0
- package/src/tests/__isograph/Query/subquery/entrypoint.ts +67 -0
- package/src/tests/__isograph/Query/subquery/output_type.ts +3 -0
- package/src/tests/__isograph/Query/subquery/param_type.ts +12 -0
- package/src/tests/__isograph/Query/subquery/parameters_type.ts +3 -0
- package/src/tests/__isograph/Query/subquery/resolver_reader.ts +47 -0
- package/src/tests/__isograph/iso.ts +22 -11
- package/src/tests/garbageCollection.test.ts +45 -39
- package/src/tests/meNameSuccessor.ts +8 -3
- package/src/tests/nodeQuery.ts +6 -4
- package/src/tests/normalizeData.test.ts +120 -0
- package/src/tests/tsconfig.json +3 -3
- package/tsconfig.json +2 -2
- package/tsconfig.pkg.json +6 -1
- package/vitest.config.ts +20 -0
@@ -1,9 +1,11 @@
|
|
1
|
-
import { LoadableField } from '../core/reader';
|
1
|
+
import { LoadableField, type ReaderAst } from '../core/reader';
|
2
2
|
import { useIsographEnvironment } from '../react/IsographEnvironmentProvider';
|
3
3
|
import { ItemCleanupPair } from '@isograph/disposable-types';
|
4
4
|
import { FragmentReference } from '../core/FragmentReference';
|
5
5
|
import { maybeUnwrapNetworkRequest } from '../react/useResult';
|
6
6
|
import { readButDoNotEvaluate } from '../core/read';
|
7
|
+
import { subscribeToAnyChange } from '../core/cache';
|
8
|
+
import { useState } from 'react';
|
7
9
|
import {
|
8
10
|
UNASSIGNED_STATE,
|
9
11
|
useUpdatableDisposableState,
|
@@ -13,31 +15,43 @@ import {
|
|
13
15
|
ReferenceCountedPointer,
|
14
16
|
} from '@isograph/reference-counted-pointer';
|
15
17
|
import { getPromiseState, readPromise } from '../core/PromiseWrapper';
|
18
|
+
import { type WithEncounteredRecords } from '../core/read';
|
19
|
+
import { useSubscribeToMultiple } from '../react/useReadAndSubscribe';
|
20
|
+
import { FetchOptions } from '../core/check';
|
16
21
|
|
17
|
-
type
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
type UseSkipLimitReturnValue<TArgs, TItem> =
|
22
|
+
type UseSkipLimitReturnValue<
|
23
|
+
TReadFromStore extends { data: object; parameters: object },
|
24
|
+
TItem,
|
25
|
+
> =
|
23
26
|
| {
|
24
27
|
readonly kind: 'Complete';
|
25
|
-
readonly fetchMore: (
|
28
|
+
readonly fetchMore: (count: number, fetchOptions?: FetchOptions) => void;
|
26
29
|
readonly results: ReadonlyArray<TItem>;
|
27
30
|
}
|
28
31
|
| {
|
29
32
|
readonly kind: 'Pending';
|
30
33
|
readonly results: ReadonlyArray<TItem>;
|
31
|
-
readonly pendingFragment: FragmentReference<
|
34
|
+
readonly pendingFragment: FragmentReference<
|
35
|
+
TReadFromStore,
|
36
|
+
ReadonlyArray<TItem>
|
37
|
+
>;
|
32
38
|
};
|
33
39
|
|
34
|
-
type ArrayFragmentReference<
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
type ArrayFragmentReference<
|
41
|
+
TReadFromStore extends { parameters: object; data: object },
|
42
|
+
TItem,
|
43
|
+
> = FragmentReference<TReadFromStore, ReadonlyArray<TItem>>;
|
44
|
+
|
45
|
+
type LoadedFragmentReferences<
|
46
|
+
TReadFromStore extends { parameters: object; data: object },
|
47
|
+
TItem,
|
48
|
+
> = ReadonlyArray<LoadedFragmentReference<TReadFromStore, TItem>>;
|
38
49
|
|
39
|
-
type
|
40
|
-
|
50
|
+
type LoadedFragmentReference<
|
51
|
+
TReadFromStore extends { parameters: object; data: object },
|
52
|
+
TItem,
|
53
|
+
> = ItemCleanupPair<
|
54
|
+
ReferenceCountedPointer<ArrayFragmentReference<TReadFromStore, TItem>>
|
41
55
|
>;
|
42
56
|
|
43
57
|
function flatten<T>(arr: ReadonlyArray<ReadonlyArray<T>>): ReadonlyArray<T> {
|
@@ -50,95 +64,136 @@ function flatten<T>(arr: ReadonlyArray<ReadonlyArray<T>>): ReadonlyArray<T> {
|
|
50
64
|
return outArray;
|
51
65
|
}
|
52
66
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
* - the data received so far.
|
59
|
-
*
|
60
|
-
* This hook will suspend if any network request is in flight.
|
61
|
-
*
|
62
|
-
* Calling fetchMore before the hook mounts is a no-op.
|
63
|
-
*
|
64
|
-
* NOTE: this hook does not subscribe to changes. This is a known
|
65
|
-
* issue. If you are running into this issue, reach out on GitHub/
|
66
|
-
* Twitter, and we'll fix the issue.
|
67
|
-
*/
|
67
|
+
type UseSkipLimitPaginationArgs = {
|
68
|
+
skip: number;
|
69
|
+
limit: number;
|
70
|
+
};
|
71
|
+
|
68
72
|
export function useSkipLimitPagination<
|
69
|
-
TArgs extends {
|
70
|
-
skip: number | void | null;
|
71
|
-
limit: number | void | null;
|
72
|
-
},
|
73
73
|
TItem,
|
74
|
+
TReadFromStore extends {
|
75
|
+
parameters: object;
|
76
|
+
data: object;
|
77
|
+
},
|
74
78
|
>(
|
75
|
-
loadableField: LoadableField<
|
76
|
-
|
79
|
+
loadableField: LoadableField<
|
80
|
+
TReadFromStore,
|
81
|
+
ReadonlyArray<TItem>,
|
82
|
+
UseSkipLimitPaginationArgs
|
83
|
+
>,
|
84
|
+
initialState?: {
|
85
|
+
skip?: number | void | null;
|
86
|
+
},
|
87
|
+
): UseSkipLimitReturnValue<TReadFromStore, TItem> {
|
77
88
|
const networkRequestOptions = {
|
78
89
|
suspendIfInFlight: true,
|
79
90
|
throwOnNetworkError: true,
|
80
91
|
};
|
81
92
|
const { state, setState } =
|
82
|
-
useUpdatableDisposableState<
|
93
|
+
useUpdatableDisposableState<
|
94
|
+
LoadedFragmentReferences<TReadFromStore, TItem>
|
95
|
+
>();
|
83
96
|
|
84
97
|
const environment = useIsographEnvironment();
|
85
98
|
|
99
|
+
// TODO move this out of useSkipLimitPagination, and pass environment and networkRequestOptions
|
100
|
+
// as parameters (or recreate networkRequestOptions)
|
86
101
|
function readCompletedFragmentReferences(
|
87
|
-
completedReferences:
|
88
|
-
ItemCleanupPair<ReferenceCountedPointer<ArrayFragmentReference<TItem>>>
|
89
|
-
>,
|
102
|
+
completedReferences: ArrayFragmentReference<TReadFromStore, TItem>[],
|
90
103
|
) {
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
104
|
+
const results = completedReferences.map((fragmentReference, i) => {
|
105
|
+
const readerWithRefetchQueries = readPromise(
|
106
|
+
fragmentReference.readerWithRefetchQueries,
|
107
|
+
);
|
108
|
+
|
109
|
+
// invariant: readOutDataAndRecords.length === completedReferences.length
|
110
|
+
const data = readOutDataAndRecords[i]?.item;
|
111
|
+
if (data == null) {
|
96
112
|
throw new Error(
|
97
|
-
'
|
98
|
-
This is indicative of a bug in Isograph.',
|
113
|
+
'Parameter data is unexpectedly null. This is indicative of a bug in Isograph.',
|
99
114
|
);
|
100
115
|
}
|
101
116
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
const data = readButDoNotEvaluate(
|
107
|
-
environment,
|
108
|
-
fragmentReference,
|
109
|
-
networkRequestOptions,
|
110
|
-
);
|
117
|
+
const firstParameter = {
|
118
|
+
data,
|
119
|
+
parameters: fragmentReference.variables,
|
120
|
+
};
|
111
121
|
|
112
|
-
|
113
|
-
|
114
|
-
)
|
122
|
+
if (
|
123
|
+
readerWithRefetchQueries.readerArtifact.kind !== 'EagerReaderArtifact'
|
124
|
+
) {
|
125
|
+
throw new Error(
|
126
|
+
`@loadable field of kind "${readerWithRefetchQueries.readerArtifact.kind}" is not supported by useSkipLimitPagination`,
|
127
|
+
);
|
128
|
+
}
|
115
129
|
|
116
|
-
return readerWithRefetchQueries.readerArtifact.resolver(
|
117
|
-
data.item,
|
118
|
-
undefined,
|
119
|
-
) as ReadonlyArray<any>;
|
130
|
+
return readerWithRefetchQueries.readerArtifact.resolver(firstParameter);
|
120
131
|
});
|
121
132
|
|
122
133
|
const items = flatten(results);
|
123
134
|
return items;
|
124
135
|
}
|
125
136
|
|
137
|
+
function subscribeCompletedFragmentReferences(
|
138
|
+
completedReferences: ArrayFragmentReference<TReadFromStore, TItem>[],
|
139
|
+
) {
|
140
|
+
return completedReferences.map(
|
141
|
+
(
|
142
|
+
fragmentReference,
|
143
|
+
i,
|
144
|
+
): {
|
145
|
+
records: WithEncounteredRecords<TReadFromStore>;
|
146
|
+
callback: (
|
147
|
+
updatedRecords: WithEncounteredRecords<TReadFromStore>,
|
148
|
+
) => void;
|
149
|
+
fragmentReference: ArrayFragmentReference<TReadFromStore, TItem>;
|
150
|
+
readerAst: ReaderAst<TItem>;
|
151
|
+
} => {
|
152
|
+
maybeUnwrapNetworkRequest(
|
153
|
+
fragmentReference.networkRequest,
|
154
|
+
networkRequestOptions,
|
155
|
+
);
|
156
|
+
|
157
|
+
const readerWithRefetchQueries = readPromise(
|
158
|
+
fragmentReference.readerWithRefetchQueries,
|
159
|
+
);
|
160
|
+
|
161
|
+
const records = readOutDataAndRecords[i];
|
162
|
+
if (records == null) {
|
163
|
+
throw new Error(
|
164
|
+
'subscribeCompletedFragmentReferences records is unexpectedly null',
|
165
|
+
);
|
166
|
+
}
|
167
|
+
|
168
|
+
return {
|
169
|
+
fragmentReference,
|
170
|
+
readerAst: readerWithRefetchQueries.readerArtifact.readerAst,
|
171
|
+
records,
|
172
|
+
callback(_data) {
|
173
|
+
rerender({});
|
174
|
+
},
|
175
|
+
};
|
176
|
+
},
|
177
|
+
);
|
178
|
+
}
|
179
|
+
|
126
180
|
const getFetchMore =
|
127
181
|
(loadedSoFar: number) =>
|
128
|
-
(
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
182
|
+
(count: number, fetchOptions?: FetchOptions): void => {
|
183
|
+
const loadedField = loadableField(
|
184
|
+
{
|
185
|
+
skip: loadedSoFar,
|
186
|
+
limit: count,
|
187
|
+
},
|
188
|
+
fetchOptions ?? {},
|
189
|
+
)[1]();
|
135
190
|
const newPointer = createReferenceCountedPointer(loadedField);
|
136
191
|
const clonedPointers = loadedReferences.map(([refCountedPointer]) => {
|
137
192
|
const clonedRefCountedPointer = refCountedPointer.cloneIfNotDisposed();
|
138
193
|
if (clonedRefCountedPointer == null) {
|
139
194
|
throw new Error(
|
140
195
|
'This reference counted pointer has already been disposed. \
|
141
|
-
|
196
|
+
This is indicative of a bug in useSkipLimitPagination.',
|
142
197
|
);
|
143
198
|
}
|
144
199
|
return clonedRefCountedPointer;
|
@@ -148,7 +203,9 @@ export function useSkipLimitPagination<
|
|
148
203
|
const totalItemCleanupPair: ItemCleanupPair<
|
149
204
|
ReadonlyArray<
|
150
205
|
ItemCleanupPair<
|
151
|
-
ReferenceCountedPointer<
|
206
|
+
ReferenceCountedPointer<
|
207
|
+
ArrayFragmentReference<TReadFromStore, TItem>
|
208
|
+
>
|
152
209
|
>
|
153
210
|
>
|
154
211
|
> = [
|
@@ -163,33 +220,73 @@ export function useSkipLimitPagination<
|
|
163
220
|
setState(totalItemCleanupPair);
|
164
221
|
};
|
165
222
|
|
223
|
+
const [, rerender] = useState({});
|
224
|
+
|
166
225
|
const loadedReferences = state === UNASSIGNED_STATE ? [] : state;
|
167
|
-
if (loadedReferences.length === 0) {
|
168
|
-
return {
|
169
|
-
kind: 'Complete',
|
170
|
-
fetchMore: getFetchMore(0),
|
171
|
-
results: [],
|
172
|
-
};
|
173
|
-
}
|
174
226
|
|
175
|
-
const mostRecentItem
|
176
|
-
|
177
|
-
|
227
|
+
const mostRecentItem:
|
228
|
+
| LoadedFragmentReference<TReadFromStore, TItem>
|
229
|
+
| undefined = loadedReferences[loadedReferences.length - 1];
|
230
|
+
const mostRecentFragmentReference =
|
231
|
+
mostRecentItem?.[0].getItemIfNotDisposed();
|
232
|
+
|
233
|
+
if (mostRecentItem && mostRecentFragmentReference === null) {
|
178
234
|
throw new Error(
|
179
235
|
'FragmentReference is unexpectedly disposed. \
|
180
|
-
|
236
|
+
This is indicative of a bug in Isograph.',
|
181
237
|
);
|
182
238
|
}
|
183
239
|
|
184
|
-
const networkRequestStatus =
|
185
|
-
mostRecentFragmentReference
|
240
|
+
const networkRequestStatus =
|
241
|
+
mostRecentFragmentReference &&
|
242
|
+
getPromiseState(mostRecentFragmentReference.networkRequest);
|
243
|
+
|
244
|
+
const slicedFragmentReferences =
|
245
|
+
networkRequestStatus?.kind === 'Ok'
|
246
|
+
? loadedReferences
|
247
|
+
: loadedReferences.slice(0, loadedReferences.length - 1);
|
248
|
+
|
249
|
+
const completedFragmentReferences = slicedFragmentReferences.map(
|
250
|
+
([pointer]) => {
|
251
|
+
const fragmentReference = pointer.getItemIfNotDisposed();
|
252
|
+
if (fragmentReference == null) {
|
253
|
+
throw new Error(
|
254
|
+
'FragmentReference is unexpectedly disposed. \
|
255
|
+
This is indicative of a bug in Isograph.',
|
256
|
+
);
|
257
|
+
}
|
258
|
+
return fragmentReference;
|
259
|
+
},
|
260
|
+
);
|
261
|
+
|
262
|
+
const readOutDataAndRecords = completedFragmentReferences.map(
|
263
|
+
(fragmentReference) =>
|
264
|
+
readButDoNotEvaluate(
|
265
|
+
environment,
|
266
|
+
fragmentReference,
|
267
|
+
networkRequestOptions,
|
268
|
+
),
|
186
269
|
);
|
270
|
+
|
271
|
+
useSubscribeToMultiple<TReadFromStore>(
|
272
|
+
subscribeCompletedFragmentReferences(completedFragmentReferences),
|
273
|
+
);
|
274
|
+
|
275
|
+
if (!networkRequestStatus) {
|
276
|
+
return {
|
277
|
+
kind: 'Complete',
|
278
|
+
fetchMore: getFetchMore(initialState?.skip ?? 0),
|
279
|
+
results: [],
|
280
|
+
};
|
281
|
+
}
|
282
|
+
|
187
283
|
switch (networkRequestStatus.kind) {
|
188
284
|
case 'Pending': {
|
189
|
-
const
|
190
|
-
|
191
|
-
|
192
|
-
);
|
285
|
+
const unsubscribe = subscribeToAnyChange(environment, () => {
|
286
|
+
unsubscribe();
|
287
|
+
rerender({});
|
288
|
+
});
|
289
|
+
|
193
290
|
return {
|
194
291
|
kind: 'Pending',
|
195
292
|
pendingFragment: mostRecentFragmentReference,
|
@@ -200,7 +297,9 @@ export function useSkipLimitPagination<
|
|
200
297
|
throw networkRequestStatus.error;
|
201
298
|
}
|
202
299
|
case 'Ok': {
|
203
|
-
const results = readCompletedFragmentReferences(
|
300
|
+
const results = readCompletedFragmentReferences(
|
301
|
+
completedFragmentReferences,
|
302
|
+
);
|
204
303
|
return {
|
205
304
|
kind: 'Complete',
|
206
305
|
results,
|
@@ -209,3 +308,45 @@ export function useSkipLimitPagination<
|
|
209
308
|
}
|
210
309
|
}
|
211
310
|
}
|
311
|
+
|
312
|
+
// @ts-ignore
|
313
|
+
function tsTests() {
|
314
|
+
type Parameters = {
|
315
|
+
readonly search: string;
|
316
|
+
readonly skip: number;
|
317
|
+
readonly limit: number;
|
318
|
+
};
|
319
|
+
|
320
|
+
let basicLoadable!: LoadableField<
|
321
|
+
{
|
322
|
+
readonly data: object;
|
323
|
+
readonly parameters: Omit<Parameters, 'search'>;
|
324
|
+
},
|
325
|
+
object[]
|
326
|
+
>;
|
327
|
+
|
328
|
+
useSkipLimitPagination(basicLoadable);
|
329
|
+
useSkipLimitPagination(basicLoadable, {});
|
330
|
+
useSkipLimitPagination(basicLoadable, { skip: 10 });
|
331
|
+
|
332
|
+
let unprovidedSearchLoadable!: LoadableField<
|
333
|
+
{
|
334
|
+
readonly data: object;
|
335
|
+
readonly parameters: Parameters;
|
336
|
+
},
|
337
|
+
object[]
|
338
|
+
>;
|
339
|
+
// @ts-expect-error
|
340
|
+
useSkipLimitPagination(unprovidedSearchLoadable);
|
341
|
+
|
342
|
+
let providedSearchLoadable!: LoadableField<
|
343
|
+
{
|
344
|
+
readonly data: object;
|
345
|
+
readonly parameters: Parameters;
|
346
|
+
},
|
347
|
+
object[],
|
348
|
+
Omit<Parameters, 'search'>
|
349
|
+
>;
|
350
|
+
|
351
|
+
useSkipLimitPagination(providedSearchLoadable);
|
352
|
+
}
|
@@ -4,17 +4,23 @@ import { FragmentReference } from '../core/FragmentReference';
|
|
4
4
|
import { useResult } from './useResult';
|
5
5
|
import { NetworkRequestReaderOptions } from '../core/read';
|
6
6
|
|
7
|
+
type IsExactlyIntrinsicAttributes<T> = T extends JSX.IntrinsicAttributes
|
8
|
+
? JSX.IntrinsicAttributes extends T
|
9
|
+
? true
|
10
|
+
: false
|
11
|
+
: false;
|
12
|
+
|
7
13
|
export function FragmentReader<
|
8
14
|
TProps extends Record<any, any>,
|
9
15
|
TEntrypoint extends IsographEntrypoint<any, React.FC<TProps>>,
|
10
16
|
>(
|
11
|
-
props: TProps extends
|
17
|
+
props: IsExactlyIntrinsicAttributes<TProps> extends true
|
12
18
|
? {
|
13
19
|
fragmentReference: FragmentReference<
|
14
20
|
ExtractReadFromStore<TEntrypoint>,
|
15
|
-
React.FC<
|
21
|
+
React.FC<TProps>
|
16
22
|
>;
|
17
|
-
additionalProps?:
|
23
|
+
additionalProps?: Record<PropertyKey, never>;
|
18
24
|
networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
|
19
25
|
}
|
20
26
|
: {
|
@@ -22,7 +28,7 @@ export function FragmentReader<
|
|
22
28
|
ExtractReadFromStore<TEntrypoint>,
|
23
29
|
React.FC<TProps>
|
24
30
|
>;
|
25
|
-
additionalProps: TProps
|
31
|
+
additionalProps: Omit<TProps, keyof JSX.IntrinsicAttributes>;
|
26
32
|
networkRequestOptions?: Partial<NetworkRequestReaderOptions>;
|
27
33
|
},
|
28
34
|
): React.ReactNode {
|
@@ -30,5 +36,8 @@ export function FragmentReader<
|
|
30
36
|
props.fragmentReference,
|
31
37
|
props.networkRequestOptions,
|
32
38
|
);
|
39
|
+
// TypeScript is not understanding that if additionalProps is Record<PropertyKey, never>,
|
40
|
+
// it means that TProps === JSX.IntrinsicAttributes.
|
41
|
+
// @ts-expect-error
|
33
42
|
return <Component {...props.additionalProps} />;
|
34
43
|
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
import { useEffect, useState } from 'react';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* This is a function that will render a component only after it commits.
|
5
|
+
* It should not be used in production. It's useful as a way to debug issues
|
6
|
+
* with NextJS, where an indefinite suspense causes the server to hang
|
7
|
+
* forever and never complete the original request.
|
8
|
+
*/
|
9
|
+
export function RenderAfterCommit__DO_NOT_USE({
|
10
|
+
children,
|
11
|
+
}: {
|
12
|
+
children: React.ReactNode;
|
13
|
+
}) {
|
14
|
+
const [show, setShow] = useState(false);
|
15
|
+
useEffect(() => setShow(true), []);
|
16
|
+
return show ? children : null;
|
17
|
+
}
|
@@ -3,16 +3,20 @@ import {
|
|
3
3
|
useUpdatableDisposableState,
|
4
4
|
} from '@isograph/react-disposable-state';
|
5
5
|
import { IsographEntrypoint } from '../core/entrypoint';
|
6
|
-
import {
|
6
|
+
import {
|
7
|
+
FragmentReference,
|
8
|
+
ExtractParameters,
|
9
|
+
} from '../core/FragmentReference';
|
7
10
|
import { useIsographEnvironment } from './IsographEnvironmentProvider';
|
8
11
|
import { ROOT_ID } from '../core/IsographEnvironment';
|
9
|
-
import {
|
12
|
+
import { maybeMakeNetworkRequest } from '../core/makeNetworkRequest';
|
10
13
|
import { wrapResolvedValue } from '../core/PromiseWrapper';
|
14
|
+
import { FetchOptions } from '../core/check';
|
11
15
|
|
12
16
|
// TODO rename this to useImperativelyLoadedEntrypoint
|
13
17
|
|
14
18
|
export function useImperativeReference<
|
15
|
-
TReadFromStore extends
|
19
|
+
TReadFromStore extends { parameters: object; data: object },
|
16
20
|
TClientFieldValue,
|
17
21
|
>(
|
18
22
|
entrypoint: IsographEntrypoint<TReadFromStore, TClientFieldValue>,
|
@@ -20,7 +24,10 @@ export function useImperativeReference<
|
|
20
24
|
fragmentReference:
|
21
25
|
| FragmentReference<TReadFromStore, TClientFieldValue>
|
22
26
|
| UnassignedState;
|
23
|
-
loadFragmentReference: (
|
27
|
+
loadFragmentReference: (
|
28
|
+
variables: ExtractParameters<TReadFromStore>,
|
29
|
+
fetchOptions?: FetchOptions,
|
30
|
+
) => void;
|
24
31
|
} {
|
25
32
|
const { state, setState } =
|
26
33
|
useUpdatableDisposableState<
|
@@ -29,11 +36,15 @@ export function useImperativeReference<
|
|
29
36
|
const environment = useIsographEnvironment();
|
30
37
|
return {
|
31
38
|
fragmentReference: state,
|
32
|
-
loadFragmentReference: (
|
33
|
-
|
39
|
+
loadFragmentReference: (
|
40
|
+
variables: ExtractParameters<TReadFromStore>,
|
41
|
+
fetchOptions?: FetchOptions,
|
42
|
+
) => {
|
43
|
+
const [networkRequest, disposeNetworkRequest] = maybeMakeNetworkRequest(
|
34
44
|
environment,
|
35
45
|
entrypoint,
|
36
46
|
variables,
|
47
|
+
fetchOptions,
|
37
48
|
);
|
38
49
|
setState([
|
39
50
|
{
|
@@ -44,7 +55,7 @@ export function useImperativeReference<
|
|
44
55
|
nestedRefetchQueries:
|
45
56
|
entrypoint.readerWithRefetchQueries.nestedRefetchQueries,
|
46
57
|
}),
|
47
|
-
root: ROOT_ID,
|
58
|
+
root: { __link: ROOT_ID, __typename: entrypoint.concreteType },
|
48
59
|
variables,
|
49
60
|
networkRequest,
|
50
61
|
},
|
@@ -1,20 +1,40 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
FragmentReference,
|
3
|
+
ExtractParameters,
|
4
|
+
} from '../core/FragmentReference';
|
2
5
|
import { useIsographEnvironment } from './IsographEnvironmentProvider';
|
3
6
|
import { IsographEntrypoint } from '../core/entrypoint';
|
4
7
|
import { getOrCreateCacheForArtifact } from '../core/cache';
|
5
8
|
import { useLazyDisposableState } from '@isograph/react-disposable-state';
|
9
|
+
import { logMessage } from '../core/logging';
|
10
|
+
import { FetchOptions } from '../core/check';
|
6
11
|
|
7
12
|
export function useLazyReference<
|
8
|
-
TReadFromStore extends
|
13
|
+
TReadFromStore extends { parameters: object; data: object },
|
9
14
|
TClientFieldValue,
|
10
15
|
>(
|
11
16
|
entrypoint: IsographEntrypoint<TReadFromStore, TClientFieldValue>,
|
12
|
-
variables:
|
17
|
+
variables: ExtractParameters<TReadFromStore>,
|
18
|
+
fetchOptions?: FetchOptions,
|
13
19
|
): {
|
14
20
|
fragmentReference: FragmentReference<TReadFromStore, TClientFieldValue>;
|
15
21
|
} {
|
16
22
|
const environment = useIsographEnvironment();
|
17
|
-
|
23
|
+
|
24
|
+
if (entrypoint?.kind !== 'Entrypoint') {
|
25
|
+
// TODO have a separate error logger
|
26
|
+
logMessage(environment, {
|
27
|
+
kind: 'NonEntrypointReceived',
|
28
|
+
entrypoint,
|
29
|
+
});
|
30
|
+
}
|
31
|
+
|
32
|
+
const cache = getOrCreateCacheForArtifact(
|
33
|
+
environment,
|
34
|
+
entrypoint,
|
35
|
+
variables,
|
36
|
+
fetchOptions,
|
37
|
+
);
|
18
38
|
|
19
39
|
return {
|
20
40
|
fragmentReference: useLazyDisposableState(cache).state,
|
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
|
|
2
2
|
import {
|
3
3
|
FragmentReference,
|
4
4
|
stableIdForFragmentReference,
|
5
|
+
ExtractData,
|
5
6
|
} from '../core/FragmentReference';
|
6
7
|
import {
|
7
8
|
NetworkRequestReaderOptions,
|
@@ -11,14 +12,18 @@ import {
|
|
11
12
|
import { useRerenderOnChange } from './useRerenderOnChange';
|
12
13
|
import { useIsographEnvironment } from './IsographEnvironmentProvider';
|
13
14
|
import { subscribe } from '../core/cache';
|
15
|
+
import type { ReaderAst } from '../core/reader';
|
14
16
|
|
15
17
|
/**
|
16
18
|
* Read the data from a fragment reference and subscribe to updates.
|
17
19
|
*/
|
18
|
-
export function useReadAndSubscribe<
|
20
|
+
export function useReadAndSubscribe<
|
21
|
+
TReadFromStore extends { parameters: object; data: object },
|
22
|
+
>(
|
19
23
|
fragmentReference: FragmentReference<TReadFromStore, any>,
|
20
24
|
networkRequestOptions: NetworkRequestReaderOptions,
|
21
|
-
|
25
|
+
readerAst: ReaderAst<TReadFromStore>,
|
26
|
+
): ExtractData<TReadFromStore> {
|
22
27
|
const environment = useIsographEnvironment();
|
23
28
|
const [readOutDataAndRecords, setReadOutDataAndRecords] = useState(() =>
|
24
29
|
readButDoNotEvaluate(environment, fragmentReference, networkRequestOptions),
|
@@ -27,23 +32,33 @@ export function useReadAndSubscribe<TReadFromStore extends Object>(
|
|
27
32
|
readOutDataAndRecords,
|
28
33
|
fragmentReference,
|
29
34
|
setReadOutDataAndRecords,
|
35
|
+
readerAst,
|
30
36
|
);
|
31
37
|
return readOutDataAndRecords.item;
|
32
38
|
}
|
33
39
|
|
34
|
-
export function useSubscribeToMultiple<
|
40
|
+
export function useSubscribeToMultiple<
|
41
|
+
TReadFromStore extends { parameters: object; data: object },
|
42
|
+
>(
|
35
43
|
items: ReadonlyArray<{
|
36
44
|
records: WithEncounteredRecords<TReadFromStore>;
|
37
45
|
callback: (updatedRecords: WithEncounteredRecords<TReadFromStore>) => void;
|
38
46
|
fragmentReference: FragmentReference<TReadFromStore, any>;
|
47
|
+
readerAst: ReaderAst<TReadFromStore>;
|
39
48
|
}>,
|
40
49
|
) {
|
41
50
|
const environment = useIsographEnvironment();
|
42
51
|
useEffect(
|
43
52
|
() => {
|
44
53
|
const cleanupFns = items.map(
|
45
|
-
({ records, callback, fragmentReference }) => {
|
46
|
-
return subscribe(
|
54
|
+
({ records, callback, fragmentReference, readerAst }) => {
|
55
|
+
return subscribe(
|
56
|
+
environment,
|
57
|
+
records,
|
58
|
+
fragmentReference,
|
59
|
+
callback,
|
60
|
+
readerAst,
|
61
|
+
);
|
47
62
|
},
|
48
63
|
);
|
49
64
|
return () => {
|
@@ -3,15 +3,19 @@ import { subscribe } from '../core/cache';
|
|
3
3
|
import { WithEncounteredRecords } from '../core/read';
|
4
4
|
import { FragmentReference } from '../core/FragmentReference';
|
5
5
|
import { useIsographEnvironment } from './IsographEnvironmentProvider';
|
6
|
+
import type { ReaderAst } from '../core/reader';
|
6
7
|
|
7
8
|
// TODO add unit tests for this. Add integration tests that test
|
8
9
|
// behavior when the encounteredRecords underneath a fragment change.
|
9
|
-
export function useRerenderOnChange<
|
10
|
+
export function useRerenderOnChange<
|
11
|
+
TReadFromStore extends { parameters: object; data: object },
|
12
|
+
>(
|
10
13
|
encounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
|
11
14
|
fragmentReference: FragmentReference<any, any>,
|
12
15
|
setEncounteredDataAndRecords: (
|
13
16
|
data: WithEncounteredRecords<TReadFromStore>,
|
14
17
|
) => void,
|
18
|
+
readerAst: ReaderAst<TReadFromStore>,
|
15
19
|
) {
|
16
20
|
const environment = useIsographEnvironment();
|
17
21
|
useEffect(() => {
|
@@ -22,6 +26,7 @@ export function useRerenderOnChange<TReadFromStore extends Object>(
|
|
22
26
|
(newEncounteredDataAndRecords) => {
|
23
27
|
setEncounteredDataAndRecords(newEncounteredDataAndRecords);
|
24
28
|
},
|
29
|
+
readerAst,
|
25
30
|
);
|
26
31
|
// Note: this is an empty array on purpose:
|
27
32
|
// - the fragment reference is stable for the life of the component
|