@isograph/react 0.0.0-main-d319cfe5 → 0.0.0-main-ad899464

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.
@@ -1,41 +1,44 @@
1
1
  import { ParentCache } from '@isograph/react-disposable-state';
2
2
  import { RetainedQuery } from './garbageCollection';
3
+ import { WithEncounteredRecords } from './read';
4
+ import { FragmentReference } from './FragmentReference';
3
5
 
4
6
  export type ComponentOrFieldName = string;
5
- type StringifiedArgs = string;
6
- type ComponentAndEncounteredRecordsCache = {
7
+ export type StringifiedArgs = string;
8
+ type ComponentCache = {
7
9
  [key: DataId]: {
8
- [key: ComponentOrFieldName]: {
9
- [key: StringifiedArgs]: ComponentAndEncounteredRecords;
10
- };
10
+ [key: ComponentOrFieldName]: { [key: StringifiedArgs]: React.FC<any> };
11
11
  };
12
12
  };
13
13
 
14
- export type ComponentAndEncounteredRecords = {
15
- component: React.FC<any>;
16
- // null if we have never read before
17
- encounteredRecordsDuringLastRead: Set<DataId> | null;
14
+ type FragmentSubscription<TReadFromStore extends Object> = {
15
+ readonly kind: 'FragmentSubscription';
16
+ readonly callback: (
17
+ newEncounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
18
+ ) => void;
19
+ /** The value read out from the previous call to readButDoNotEvaluate */
20
+ readonly encounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>;
21
+ readonly fragmentReference: FragmentReference<TReadFromStore, any>;
18
22
  };
19
-
20
- type CallbackAndRecords = {
21
- callback: () => void;
22
- records: Set<DataId> | null;
23
+ type AnyRecordSubscription = {
24
+ readonly kind: 'AnyRecords';
25
+ readonly callback: () => void;
23
26
  };
24
- type Subscriptions = Set<CallbackAndRecords>;
27
+
28
+ type Subscription = FragmentSubscription<Object> | AnyRecordSubscription;
29
+ type Subscriptions = Set<Subscription>;
25
30
  type SuspenseCache = { [index: string]: ParentCache<any> };
26
31
 
27
32
  export type IsographEnvironment = {
28
33
  store: IsographStore;
29
34
  networkFunction: IsographNetworkFunction;
30
35
  missingFieldHandler: MissingFieldHandler | null;
36
+ componentCache: ComponentCache;
31
37
  subscriptions: Subscriptions;
38
+ suspenseCache: SuspenseCache;
32
39
  retainedQueries: Set<RetainedQuery>;
33
40
  gcBuffer: Array<RetainedQuery>;
34
41
  gcBufferSize: number;
35
-
36
- // These caches should be moved into the store somehow
37
- componentAndEncounteredRecordsCache: ComponentAndEncounteredRecordsCache;
38
- suspenseCache: SuspenseCache;
39
42
  };
40
43
 
41
44
  export type MissingFieldHandler = (
@@ -95,7 +98,7 @@ export function createIsographEnvironment(
95
98
  store,
96
99
  networkFunction,
97
100
  missingFieldHandler: missingFieldHandler ?? null,
98
- componentAndEncounteredRecordsCache: {},
101
+ componentCache: {},
99
102
  subscriptions: new Set(),
100
103
  suspenseCache: {},
101
104
  retainedQueries: new Set(),
@@ -0,0 +1,78 @@
1
+ export function areEqualWithDeepComparison(
2
+ oldItem: unknown,
3
+ newItem: unknown,
4
+ ): boolean {
5
+ if (newItem === null) {
6
+ return oldItem === null;
7
+ }
8
+
9
+ if (newItem === undefined) {
10
+ return oldItem === undefined;
11
+ }
12
+
13
+ if (Array.isArray(newItem)) {
14
+ if (!Array.isArray(oldItem)) {
15
+ return false;
16
+ }
17
+
18
+ return areEqualArraysWithDeepComparison(oldItem, newItem);
19
+ }
20
+
21
+ if (typeof newItem === 'object') {
22
+ if (typeof oldItem !== 'object') {
23
+ return false;
24
+ }
25
+
26
+ if (oldItem === null) {
27
+ return false;
28
+ }
29
+
30
+ return areEqualObjectsWithDeepComparison(oldItem, newItem);
31
+ }
32
+
33
+ return newItem === oldItem;
34
+ }
35
+
36
+ export function areEqualArraysWithDeepComparison(
37
+ oldItems: ReadonlyArray<unknown>,
38
+ newItems: ReadonlyArray<unknown>,
39
+ ): boolean {
40
+ if (newItems.length !== oldItems.length) {
41
+ return false;
42
+ }
43
+
44
+ for (let i = 0; i < newItems.length; i++) {
45
+ if (!areEqualWithDeepComparison(oldItems[i], newItems[i])) {
46
+ return false;
47
+ }
48
+ }
49
+
50
+ return true;
51
+ }
52
+
53
+ export function areEqualObjectsWithDeepComparison(
54
+ oldItemObject: object,
55
+ newItemObject: object,
56
+ ): boolean {
57
+ const oldKeys = Object.keys(oldItemObject);
58
+ const newKeys = Object.keys(newItemObject);
59
+
60
+ if (oldKeys.length !== newKeys.length) {
61
+ return false;
62
+ }
63
+
64
+ for (const oldKey of oldKeys) {
65
+ if (!(oldKey in newItemObject)) {
66
+ return false;
67
+ }
68
+ // @ts-expect-error
69
+ const oldValue = oldItemObject[oldKey];
70
+ // @ts-expect-error
71
+ const newValue = newItemObject[oldKey];
72
+
73
+ if (!areEqualWithDeepComparison(oldValue, newValue)) {
74
+ return false;
75
+ }
76
+ }
77
+ return true;
78
+ }
package/src/cache.ts CHANGED
@@ -28,6 +28,9 @@ import {
28
28
  } from './entrypoint';
29
29
  import { ReaderLinkedField, ReaderScalarField } from './reader';
30
30
  import { Argument, ArgumentValue } from './util';
31
+ import { WithEncounteredRecords, readButDoNotEvaluate } from './read';
32
+ import { FragmentReference } from './FragmentReference';
33
+ import { areEqualObjectsWithDeepComparison } from './areEqualWithDeepComparison';
31
34
 
32
35
  declare global {
33
36
  interface Window {
@@ -222,22 +225,41 @@ function normalizeData(
222
225
  return encounteredIds;
223
226
  }
224
227
 
225
- export function subscribe(
228
+ export function subscribeToAnyChange(
226
229
  environment: IsographEnvironment,
227
- encounteredRecords: Set<DataId> | null,
228
230
  callback: () => void,
229
231
  ): () => void {
230
- const callbackAndRecords = {
232
+ const subscription = {
233
+ kind: 'AnyRecords',
231
234
  callback,
232
- records: encounteredRecords,
233
- };
234
- environment.subscriptions.add(callbackAndRecords);
235
- return () => environment.subscriptions.delete(callbackAndRecords);
235
+ } as const;
236
+ environment.subscriptions.add(subscription);
237
+ return () => environment.subscriptions.delete(subscription);
238
+ }
239
+
240
+ export function subscribe<TReadFromStore extends Object>(
241
+ environment: IsographEnvironment,
242
+ encounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
243
+ fragmentReference: FragmentReference<TReadFromStore, any>,
244
+ callback: (
245
+ newEncounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
246
+ ) => void,
247
+ ): () => void {
248
+ const fragmentSubscription = {
249
+ kind: 'FragmentSubscription',
250
+ callback,
251
+ encounteredDataAndRecords,
252
+ fragmentReference,
253
+ } as const;
254
+ // @ts-expect-error
255
+ environment.subscriptions.add(fragmentSubscription);
256
+ // @ts-expect-error
257
+ return () => environment.subscriptions.delete(fragmentSubscription);
236
258
  }
237
259
 
238
260
  export function onNextChange(environment: IsographEnvironment): Promise<void> {
239
261
  return new Promise((resolve) => {
240
- const unsubscribe = subscribe(environment, null, () => {
262
+ const unsubscribe = subscribeToAnyChange(environment, () => {
241
263
  unsubscribe();
242
264
  resolve();
243
265
  });
@@ -248,11 +270,57 @@ function callSubscriptions(
248
270
  environment: IsographEnvironment,
249
271
  recordsEncounteredWhenNormalizing: Set<DataId>,
250
272
  ) {
251
- environment.subscriptions.forEach(({ callback, records }) => {
252
- if (records === null) {
253
- callback();
254
- } else if (hasOverlappingIds(recordsEncounteredWhenNormalizing, records)) {
255
- callback();
273
+ environment.subscriptions.forEach((subscription) => {
274
+ switch (subscription.kind) {
275
+ case 'FragmentSubscription': {
276
+ // TODO if there are multiple components subscribed to the same
277
+ // fragment, we will call readButNotEvaluate multiple times. We
278
+ // should fix that.
279
+ if (
280
+ hasOverlappingIds(
281
+ recordsEncounteredWhenNormalizing,
282
+ subscription.encounteredDataAndRecords.encounteredRecords,
283
+ )
284
+ ) {
285
+ const newEncounteredDataAndRecords = readButDoNotEvaluate(
286
+ environment,
287
+ subscription.fragmentReference,
288
+ );
289
+
290
+ if (
291
+ !areEqualObjectsWithDeepComparison(
292
+ subscription.encounteredDataAndRecords.item,
293
+ newEncounteredDataAndRecords.item,
294
+ )
295
+ ) {
296
+ if (typeof window !== 'undefined' && window.__LOG) {
297
+ console.log('Deep equality - No', {
298
+ fragmentReference: subscription.fragmentReference,
299
+ old: subscription.encounteredDataAndRecords.item,
300
+ new: newEncounteredDataAndRecords.item,
301
+ });
302
+ }
303
+ // TODO deep compare values
304
+ subscription.callback(newEncounteredDataAndRecords);
305
+ } else {
306
+ if (typeof window !== 'undefined' && window.__LOG) {
307
+ console.log('Deep equality - Yes', {
308
+ fragmentReference: subscription.fragmentReference,
309
+ old: subscription.encounteredDataAndRecords.item,
310
+ });
311
+ }
312
+ }
313
+ }
314
+ return;
315
+ }
316
+ case 'AnyRecords': {
317
+ return subscription.callback();
318
+ }
319
+ default: {
320
+ // @ts-expect-error(6133)
321
+ const _: never = subscription;
322
+ throw new Error('Unexpected case');
323
+ }
256
324
  }
257
325
  });
258
326
  }
@@ -1,61 +1,50 @@
1
1
  import { stableCopy } from './cache';
2
- import { RefetchQueryArtifactWrapper } from './entrypoint';
3
- import {
4
- IsographEnvironment,
5
- DataId,
6
- ComponentAndEncounteredRecords,
7
- } from './IsographEnvironment';
8
- import { readButDoNotEvaluate } from './read';
9
- import { ReaderArtifact } from './reader';
10
- import { useRerenderWhenEncounteredRecordChanges } from './useRerenderWhenEncounteredRecordChanges';
2
+ import { IsographEnvironment } from './IsographEnvironment';
3
+ import { FragmentReference } from './FragmentReference';
4
+ import { useReadAndSubscribe } from './useReadAndSubscribe';
11
5
 
12
6
  export function getOrCreateCachedComponent(
13
7
  environment: IsographEnvironment,
14
- rootId: DataId,
15
8
  componentName: string,
16
- readerArtifact: ReaderArtifact<any, any>,
17
- variables: { [key: string]: string },
18
- resolverRefetchQueries: RefetchQueryArtifactWrapper[],
9
+ fragmentReference: FragmentReference<any, any>,
19
10
  ): React.FC<any> {
20
- const cachedComponentsById = environment.componentAndEncounteredRecordsCache;
21
- const stringifiedArgs = JSON.stringify(stableCopy(variables));
22
- cachedComponentsById[rootId] = cachedComponentsById[rootId] ?? {};
23
- const componentsByName = cachedComponentsById[rootId];
11
+ // cachedComponentsById is a three layer cache: id, then component name, then
12
+ // stringified args. These three, together, uniquely identify a read at a given
13
+ // time.
14
+ const cachedComponentsById = environment.componentCache;
15
+
16
+ cachedComponentsById[fragmentReference.root] =
17
+ cachedComponentsById[fragmentReference.root] ?? {};
18
+ const componentsByName = cachedComponentsById[fragmentReference.root];
19
+
24
20
  componentsByName[componentName] = componentsByName[componentName] ?? {};
25
21
  const byArgs = componentsByName[componentName];
22
+
23
+ const stringifiedArgs = JSON.stringify(
24
+ stableCopy(fragmentReference.variables),
25
+ );
26
26
  byArgs[stringifiedArgs] =
27
27
  byArgs[stringifiedArgs] ??
28
- ((): ComponentAndEncounteredRecords => {
28
+ (() => {
29
29
  function Component(additionalRuntimeProps: { [key: string]: any }) {
30
- const { item: data, encounteredRecords } = readButDoNotEvaluate(
31
- environment,
32
- {
33
- kind: 'FragmentReference',
34
- readerArtifact: readerArtifact,
35
- root: rootId,
36
- variables,
37
- nestedRefetchQueries: resolverRefetchQueries,
38
- },
39
- byArgs[stringifiedArgs],
40
- );
41
-
42
- useRerenderWhenEncounteredRecordChanges(
43
- environment,
44
- encounteredRecords,
45
- );
30
+ const data = useReadAndSubscribe(environment, fragmentReference);
46
31
 
47
32
  if (typeof window !== 'undefined' && window.__LOG) {
48
- console.log('Component re-rendered: ' + componentName + ' ' + rootId);
33
+ console.log(
34
+ 'Component re-rendered: ' +
35
+ componentName +
36
+ ' ' +
37
+ fragmentReference.root,
38
+ );
49
39
  }
50
40
 
51
- return readerArtifact.resolver(data, additionalRuntimeProps);
41
+ return fragmentReference.readerArtifact.resolver(
42
+ data,
43
+ additionalRuntimeProps,
44
+ );
52
45
  }
53
- Component.displayName = `${componentName} (id: ${rootId}) @component`;
54
- return {
55
- component: Component,
56
- encounteredRecordsDuringLastRead: null,
57
- };
46
+ Component.displayName = `${componentName} (id: ${fragmentReference.root}) @component`;
47
+ return Component;
58
48
  })();
59
-
60
- return byArgs[stringifiedArgs].component;
49
+ return byArgs[stringifiedArgs];
61
50
  }
package/src/index.ts CHANGED
@@ -47,7 +47,7 @@ export {
47
47
  RefetchQueryArtifact,
48
48
  RefetchQueryArtifactWrapper,
49
49
  } from './entrypoint';
50
- export { read, readButDoNotEvaluate } from './read';
50
+ export { readButDoNotEvaluate } from './read';
51
51
  export { useResult } from './useResult';
52
52
  export { type FragmentReference } from './FragmentReference';
53
53
  export { useLazyReference } from './useLazyReference';
@@ -58,4 +58,4 @@ export {
58
58
  ArgumentValue,
59
59
  Arguments,
60
60
  } from './util';
61
- export { useRerenderWhenEncounteredRecordChanges } from './useRerenderWhenEncounteredRecordChanges';
61
+ export { useRerenderOnChange } from './useRerenderOnChange';
package/src/read.ts CHANGED
@@ -4,7 +4,6 @@ import { RefetchQueryArtifactWrapper } from './entrypoint';
4
4
  import { FragmentReference } from './FragmentReference';
5
5
  import {
6
6
  assertLink,
7
- ComponentAndEncounteredRecords,
8
7
  DataId,
9
8
  defaultMissingFieldHandler,
10
9
  IsographEnvironment,
@@ -16,52 +15,9 @@ export type WithEncounteredRecords<T> = {
16
15
  item: T;
17
16
  };
18
17
 
19
- export function read<TReadFromStore extends Object, TClientFieldValue>(
20
- environment: IsographEnvironment,
21
- fragmentReference: FragmentReference<TReadFromStore, TClientFieldValue>,
22
- ): WithEncounteredRecords<TClientFieldValue> {
23
- const variant = fragmentReference.readerArtifact.variant;
24
- if (variant.kind === 'Eager') {
25
- const mutableEncounteredRecords = new Set<DataId>();
26
- const data = readData(
27
- environment,
28
- fragmentReference.readerArtifact.readerAst,
29
- fragmentReference.root,
30
- fragmentReference.variables ?? {},
31
- fragmentReference.nestedRefetchQueries,
32
- mutableEncounteredRecords,
33
- );
34
- if (data.kind === 'MissingData') {
35
- throw onNextChange(environment);
36
- } else {
37
- return {
38
- encounteredRecords: mutableEncounteredRecords,
39
- // @ts-expect-error This not properly typed yet
40
- item: fragmentReference.readerArtifact.resolver(data.data),
41
- };
42
- }
43
- } else if (variant.kind === 'Component') {
44
- return {
45
- // @ts-ignore
46
- item: getOrCreateCachedComponent(
47
- environment,
48
- fragmentReference.root,
49
- variant.componentName,
50
- fragmentReference.readerArtifact,
51
- fragmentReference.variables ?? {},
52
- fragmentReference.nestedRefetchQueries,
53
- ),
54
- encounteredRecords: new Set(),
55
- };
56
- }
57
- // Why can't Typescript realize that this is unreachable??
58
- throw new Error('This is unreachable');
59
- }
60
-
61
18
  export function readButDoNotEvaluate<TReadFromStore extends Object>(
62
19
  environment: IsographEnvironment,
63
20
  reference: FragmentReference<TReadFromStore, unknown>,
64
- encounteredRecordCache: ComponentAndEncounteredRecords,
65
21
  ): WithEncounteredRecords<TReadFromStore> {
66
22
  const mutableEncounteredRecords = new Set<DataId>();
67
23
  const response = readData(
@@ -78,42 +34,13 @@ export function readButDoNotEvaluate<TReadFromStore extends Object>(
78
34
  if (response.kind === 'MissingData') {
79
35
  throw onNextChange(environment);
80
36
  } else {
81
- const encounteredRecords = compareAndUpdateEncounteredRecords(
82
- encounteredRecordCache,
83
- mutableEncounteredRecords,
84
- );
85
-
86
37
  return {
87
- encounteredRecords,
38
+ encounteredRecords: mutableEncounteredRecords,
88
39
  item: response.data,
89
40
  };
90
41
  }
91
42
  }
92
43
 
93
- function compareAndUpdateEncounteredRecords(
94
- encounteredRecordCache: ComponentAndEncounteredRecords,
95
- newEncounteredRecords: Set<DataId>,
96
- ): Set<DataId> {
97
- const { encounteredRecordsDuringLastRead } = encounteredRecordCache;
98
- if (
99
- encounteredRecordsDuringLastRead == null ||
100
- encounteredRecordsDuringLastRead.size != newEncounteredRecords.size
101
- ) {
102
- encounteredRecordCache.encounteredRecordsDuringLastRead =
103
- newEncounteredRecords;
104
- return newEncounteredRecords;
105
- } else {
106
- for (const item of newEncounteredRecords) {
107
- if (!encounteredRecordsDuringLastRead.has(item)) {
108
- encounteredRecordCache.encounteredRecordsDuringLastRead =
109
- newEncounteredRecords;
110
- return newEncounteredRecords;
111
- }
112
- }
113
- return encounteredRecordsDuringLastRead;
114
- }
115
- }
116
-
117
44
  type ReadDataResult<TReadFromStore> =
118
45
  | {
119
46
  kind: 'Success';
@@ -276,9 +203,6 @@ function readData<TReadFromStore>(
276
203
  [],
277
204
  mutableEncounteredRecords,
278
205
  );
279
- if (typeof window !== 'undefined' && window.__LOG) {
280
- console.log('refetch field data', data, field);
281
- }
282
206
  if (data.kind === 'MissingData') {
283
207
  return {
284
208
  kind: 'MissingData',
@@ -319,9 +243,6 @@ function readData<TReadFromStore>(
319
243
  [],
320
244
  mutableEncounteredRecords,
321
245
  );
322
- if (typeof window !== 'undefined' && window.__LOG) {
323
- console.log('refetch field data', data, field);
324
- }
325
246
  if (data.kind === 'MissingData') {
326
247
  return {
327
248
  kind: 'MissingData',
@@ -376,11 +297,14 @@ function readData<TReadFromStore>(
376
297
  } else if (variant.kind === 'Component') {
377
298
  target[field.alias] = getOrCreateCachedComponent(
378
299
  environment,
379
- root,
380
300
  variant.componentName,
381
- field.readerArtifact,
382
- variables,
383
- resolverRefetchQueries,
301
+ {
302
+ kind: 'FragmentReference',
303
+ readerArtifact: field.readerArtifact,
304
+ root,
305
+ variables,
306
+ nestedRefetchQueries: resolverRefetchQueries,
307
+ } as const,
384
308
  );
385
309
  }
386
310
  break;
package/src/reader.ts CHANGED
@@ -5,10 +5,9 @@ import { Arguments } from './util';
5
5
  // non-@component and refetch resolvers
6
6
  export type ReaderArtifact<TReadFromStore extends Object, TClientFieldValue> = {
7
7
  kind: 'ReaderArtifact';
8
- // The DataID of the parent + the fieldName + the variables are enough
9
- // to uniquely identify a call to read(...) at a given time.
10
8
  fieldName: ComponentOrFieldName;
11
9
  readerAst: ReaderAst<TReadFromStore>;
10
+ // TODO move resolver into the variant
12
11
  resolver: (data: TReadFromStore, runtimeProps: any) => TClientFieldValue;
13
12
  variant: ReaderResolverVariant;
14
13
  };
@@ -0,0 +1,25 @@
1
+ import { useState } from 'react';
2
+ import { FragmentReference } from './FragmentReference';
3
+ import { IsographEnvironment } from './IsographEnvironment';
4
+ import { readButDoNotEvaluate } from './read';
5
+ import { useRerenderOnChange } from './useRerenderOnChange';
6
+
7
+ /**
8
+ * Read the data from a fragment reference and subscribe to updates.
9
+ * Does not pass the data to the fragment reference's resolver function.
10
+ */
11
+ export function useReadAndSubscribe<TReadFromStore extends Object>(
12
+ environment: IsographEnvironment,
13
+ fragmentReference: FragmentReference<TReadFromStore, any>,
14
+ ): TReadFromStore {
15
+ const [readOutDataAndRecords, setReadOutDataAndRecords] = useState(() =>
16
+ readButDoNotEvaluate(environment, fragmentReference),
17
+ );
18
+ useRerenderOnChange(
19
+ environment,
20
+ readOutDataAndRecords,
21
+ fragmentReference,
22
+ setReadOutDataAndRecords,
23
+ );
24
+ return readOutDataAndRecords.item;
25
+ }
@@ -0,0 +1,33 @@
1
+ import { useEffect } from 'react';
2
+ import { IsographEnvironment } from './IsographEnvironment';
3
+ import { subscribe } from './cache';
4
+ import { WithEncounteredRecords } from './read';
5
+ import { FragmentReference } from './FragmentReference';
6
+
7
+ // TODO add unit tests for this. Add integration tests that test
8
+ // behavior when the encounteredRecords underneath a fragment change.
9
+ export function useRerenderOnChange<TReadFromStore extends Object>(
10
+ environment: IsographEnvironment,
11
+ encounteredDataAndRecords: WithEncounteredRecords<TReadFromStore>,
12
+ fragmentReference: FragmentReference<any, any>,
13
+ setEncounteredDataAndRecords: (
14
+ data: WithEncounteredRecords<TReadFromStore>,
15
+ ) => void,
16
+ ) {
17
+ useEffect(() => {
18
+ return subscribe(
19
+ environment,
20
+ encounteredDataAndRecords,
21
+ fragmentReference,
22
+ (newEncounteredDataAndRecords) => {
23
+ setEncounteredDataAndRecords(newEncounteredDataAndRecords);
24
+ },
25
+ );
26
+ // Note: this is an empty array on purpose:
27
+ // - the fragment reference is stable for the life of the component
28
+ // - ownership of encounteredDataAndRecords is transferred into the
29
+ // environment
30
+ // - though maybe we need to include setEncounteredDataAndRecords in
31
+ // the dependency array
32
+ }, []);
33
+ }
package/src/useResult.ts CHANGED
@@ -1,19 +1,26 @@
1
1
  import { useIsographEnvironment } from './IsographEnvironmentProvider';
2
- import { read } from './read';
3
2
  import { FragmentReference } from './FragmentReference';
4
- import { useRerenderWhenEncounteredRecordChanges } from './useRerenderWhenEncounteredRecordChanges';
3
+ import { getOrCreateCachedComponent } from './componentCache';
4
+ import { useReadAndSubscribe } from './useReadAndSubscribe';
5
5
 
6
6
  export function useResult<TReadFromStore extends Object, TClientFieldValue>(
7
7
  fragmentReference: FragmentReference<TReadFromStore, TClientFieldValue>,
8
8
  ): TClientFieldValue {
9
9
  const environment = useIsographEnvironment();
10
10
 
11
- const { item: data, encounteredRecords } = read(
12
- environment,
13
- fragmentReference,
14
- );
15
-
16
- useRerenderWhenEncounteredRecordChanges(environment, encounteredRecords);
17
-
18
- return data;
11
+ switch (fragmentReference.readerArtifact.variant.kind) {
12
+ case 'Component': {
13
+ // @ts-expect-error
14
+ return getOrCreateCachedComponent(
15
+ environment,
16
+ fragmentReference.readerArtifact.variant.componentName,
17
+ fragmentReference,
18
+ );
19
+ }
20
+ case 'Eager': {
21
+ const data = useReadAndSubscribe(environment, fragmentReference);
22
+ // @ts-expect-error resolver is incorrectly typed in ReaderArtifact
23
+ return fragmentReference.readerArtifact.resolver(data);
24
+ }
25
+ }
19
26
  }
@@ -1,2 +0,0 @@
1
- import { DataId, IsographEnvironment } from './IsographEnvironment';
2
- export declare function useRerenderWhenEncounteredRecordChanges(environment: IsographEnvironment, encounteredRecords: Set<DataId>): void;
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useRerenderWhenEncounteredRecordChanges = void 0;
4
- const react_1 = require("react");
5
- const cache_1 = require("./cache");
6
- // TODO add unit tests for this. Add integration tests that test
7
- // behavior when the encounteredRecords underneath a fragment change.
8
- function useRerenderWhenEncounteredRecordChanges(environment, encounteredRecords) {
9
- const [, setState] = (0, react_1.useState)();
10
- (0, react_1.useEffect)(() => {
11
- return (0, cache_1.subscribe)(environment, encounteredRecords, () => {
12
- return setState({});
13
- });
14
- }, [encounteredRecords]);
15
- }
16
- exports.useRerenderWhenEncounteredRecordChanges = useRerenderWhenEncounteredRecordChanges;
@@ -1,17 +0,0 @@
1
- import { useEffect, useState } from 'react';
2
- import { DataId, IsographEnvironment } from './IsographEnvironment';
3
- import { subscribe } from './cache';
4
-
5
- // TODO add unit tests for this. Add integration tests that test
6
- // behavior when the encounteredRecords underneath a fragment change.
7
- export function useRerenderWhenEncounteredRecordChanges(
8
- environment: IsographEnvironment,
9
- encounteredRecords: Set<DataId>,
10
- ) {
11
- const [, setState] = useState<object | void>();
12
- useEffect(() => {
13
- return subscribe(environment, encounteredRecords, () => {
14
- return setState({});
15
- });
16
- }, [encounteredRecords]);
17
- }