@isograph/react 0.0.0-main-0a7c1aef → 0.0.0-main-d319cfe5

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,15 +1,19 @@
1
1
  /// <reference types="react" />
2
2
  import { ParentCache } from '@isograph/react-disposable-state';
3
3
  import { RetainedQuery } from './garbageCollection';
4
- type ComponentName = string;
4
+ export type ComponentOrFieldName = string;
5
5
  type StringifiedArgs = string;
6
- type ComponentCache = {
6
+ type ComponentAndEncounteredRecordsCache = {
7
7
  [key: DataId]: {
8
- [key: ComponentName]: {
9
- [key: StringifiedArgs]: React.FC<any>;
8
+ [key: ComponentOrFieldName]: {
9
+ [key: StringifiedArgs]: ComponentAndEncounteredRecords;
10
10
  };
11
11
  };
12
12
  };
13
+ export type ComponentAndEncounteredRecords = {
14
+ component: React.FC<any>;
15
+ encounteredRecordsDuringLastRead: Set<DataId> | null;
16
+ };
13
17
  type CallbackAndRecords = {
14
18
  callback: () => void;
15
19
  records: Set<DataId> | null;
@@ -22,12 +26,12 @@ export type IsographEnvironment = {
22
26
  store: IsographStore;
23
27
  networkFunction: IsographNetworkFunction;
24
28
  missingFieldHandler: MissingFieldHandler | null;
25
- componentCache: ComponentCache;
26
29
  subscriptions: Subscriptions;
27
- suspenseCache: SuspenseCache;
28
30
  retainedQueries: Set<RetainedQuery>;
29
31
  gcBuffer: Array<RetainedQuery>;
30
32
  gcBufferSize: number;
33
+ componentAndEncounteredRecordsCache: ComponentAndEncounteredRecordsCache;
34
+ suspenseCache: SuspenseCache;
31
35
  };
32
36
  export type MissingFieldHandler = (storeRecord: StoreRecord, root: DataId, fieldName: string, arguments_: {
33
37
  [index: string]: any;
@@ -8,7 +8,7 @@ function createIsographEnvironment(store, networkFunction, missingFieldHandler)
8
8
  store,
9
9
  networkFunction,
10
10
  missingFieldHandler: missingFieldHandler !== null && missingFieldHandler !== void 0 ? missingFieldHandler : null,
11
- componentCache: {},
11
+ componentAndEncounteredRecordsCache: {},
12
12
  subscriptions: new Set(),
13
13
  suspenseCache: {},
14
14
  retainedQueries: new Set(),
@@ -4,4 +4,4 @@ import { IsographEnvironment, DataId } from './IsographEnvironment';
4
4
  import { ReaderArtifact } from './reader';
5
5
  export declare function getOrCreateCachedComponent(environment: IsographEnvironment, rootId: DataId, componentName: string, readerArtifact: ReaderArtifact<any, any>, variables: {
6
6
  [key: string]: string;
7
- }, resolverRefetchQueries: RefetchQueryArtifactWrapper[]): import("react").FC<any>;
7
+ }, resolverRefetchQueries: RefetchQueryArtifactWrapper[]): React.FC<any>;
@@ -6,7 +6,7 @@ const read_1 = require("./read");
6
6
  const useRerenderWhenEncounteredRecordChanges_1 = require("./useRerenderWhenEncounteredRecordChanges");
7
7
  function getOrCreateCachedComponent(environment, rootId, componentName, readerArtifact, variables, resolverRefetchQueries) {
8
8
  var _a, _b, _c;
9
- const cachedComponentsById = environment.componentCache;
9
+ const cachedComponentsById = environment.componentAndEncounteredRecordsCache;
10
10
  const stringifiedArgs = JSON.stringify((0, cache_1.stableCopy)(variables));
11
11
  cachedComponentsById[rootId] = (_a = cachedComponentsById[rootId]) !== null && _a !== void 0 ? _a : {};
12
12
  const componentsByName = cachedComponentsById[rootId];
@@ -21,7 +21,7 @@ function getOrCreateCachedComponent(environment, rootId, componentName, readerAr
21
21
  root: rootId,
22
22
  variables,
23
23
  nestedRefetchQueries: resolverRefetchQueries,
24
- });
24
+ }, byArgs[stringifiedArgs]);
25
25
  (0, useRerenderWhenEncounteredRecordChanges_1.useRerenderWhenEncounteredRecordChanges)(environment, encounteredRecords);
26
26
  if (typeof window !== 'undefined' && window.__LOG) {
27
27
  console.log('Component re-rendered: ' + componentName + ' ' + rootId);
@@ -29,8 +29,11 @@ function getOrCreateCachedComponent(environment, rootId, componentName, readerAr
29
29
  return readerArtifact.resolver(data, additionalRuntimeProps);
30
30
  }
31
31
  Component.displayName = `${componentName} (id: ${rootId}) @component`;
32
- return Component;
32
+ return {
33
+ component: Component,
34
+ encounteredRecordsDuringLastRead: null,
35
+ };
33
36
  })();
34
- return byArgs[stringifiedArgs];
37
+ return byArgs[stringifiedArgs].component;
35
38
  }
36
39
  exports.getOrCreateCachedComponent = getOrCreateCachedComponent;
package/dist/read.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { FragmentReference } from './FragmentReference';
2
- import { DataId, IsographEnvironment } from './IsographEnvironment';
2
+ import { ComponentAndEncounteredRecords, DataId, IsographEnvironment } from './IsographEnvironment';
3
3
  export type WithEncounteredRecords<T> = {
4
4
  encounteredRecords: Set<DataId>;
5
5
  item: T;
6
6
  };
7
7
  export declare function read<TReadFromStore extends Object, TClientFieldValue>(environment: IsographEnvironment, fragmentReference: FragmentReference<TReadFromStore, TClientFieldValue>): WithEncounteredRecords<TClientFieldValue>;
8
- export declare function readButDoNotEvaluate<TReadFromStore extends Object>(environment: IsographEnvironment, reference: FragmentReference<TReadFromStore, unknown>): WithEncounteredRecords<TReadFromStore>;
8
+ export declare function readButDoNotEvaluate<TReadFromStore extends Object>(environment: IsographEnvironment, reference: FragmentReference<TReadFromStore, unknown>, encounteredRecordCache: ComponentAndEncounteredRecords): WithEncounteredRecords<TReadFromStore>;
package/dist/read.js CHANGED
@@ -32,7 +32,7 @@ function read(environment, fragmentReference) {
32
32
  throw new Error('This is unreachable');
33
33
  }
34
34
  exports.read = read;
35
- function readButDoNotEvaluate(environment, reference) {
35
+ function readButDoNotEvaluate(environment, reference, encounteredRecordCache) {
36
36
  var _a;
37
37
  const mutableEncounteredRecords = new Set();
38
38
  const response = readData(environment, reference.readerArtifact.readerAst, reference.root, (_a = reference.variables) !== null && _a !== void 0 ? _a : {}, reference.nestedRefetchQueries, mutableEncounteredRecords);
@@ -43,13 +43,33 @@ function readButDoNotEvaluate(environment, reference) {
43
43
  throw (0, cache_1.onNextChange)(environment);
44
44
  }
45
45
  else {
46
+ const encounteredRecords = compareAndUpdateEncounteredRecords(encounteredRecordCache, mutableEncounteredRecords);
46
47
  return {
47
- encounteredRecords: mutableEncounteredRecords,
48
+ encounteredRecords,
48
49
  item: response.data,
49
50
  };
50
51
  }
51
52
  }
52
53
  exports.readButDoNotEvaluate = readButDoNotEvaluate;
54
+ function compareAndUpdateEncounteredRecords(encounteredRecordCache, newEncounteredRecords) {
55
+ const { encounteredRecordsDuringLastRead } = encounteredRecordCache;
56
+ if (encounteredRecordsDuringLastRead == null ||
57
+ encounteredRecordsDuringLastRead.size != newEncounteredRecords.size) {
58
+ encounteredRecordCache.encounteredRecordsDuringLastRead =
59
+ newEncounteredRecords;
60
+ return newEncounteredRecords;
61
+ }
62
+ else {
63
+ for (const item of newEncounteredRecords) {
64
+ if (!encounteredRecordsDuringLastRead.has(item)) {
65
+ encounteredRecordCache.encounteredRecordsDuringLastRead =
66
+ newEncounteredRecords;
67
+ return newEncounteredRecords;
68
+ }
69
+ }
70
+ return encounteredRecordsDuringLastRead;
71
+ }
72
+ }
53
73
  function readData(environment, ast, root, variables, nestedRefetchQueries, mutableEncounteredRecords) {
54
74
  var _a, _b, _c, _d, _e;
55
75
  mutableEncounteredRecords.add(root);
package/dist/reader.d.ts CHANGED
@@ -1,6 +1,8 @@
1
+ import { ComponentOrFieldName } from './IsographEnvironment';
1
2
  import { Arguments } from './util';
2
3
  export type ReaderArtifact<TReadFromStore extends Object, TClientFieldValue> = {
3
4
  kind: 'ReaderArtifact';
5
+ fieldName: ComponentOrFieldName;
4
6
  readerAst: ReaderAst<TReadFromStore>;
5
7
  resolver: (data: TReadFromStore, runtimeProps: any) => TClientFieldValue;
6
8
  variant: ReaderResolverVariant;
@@ -3,15 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useRerenderWhenEncounteredRecordChanges = void 0;
4
4
  const react_1 = require("react");
5
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.
6
8
  function useRerenderWhenEncounteredRecordChanges(environment, encounteredRecords) {
7
9
  const [, setState] = (0, react_1.useState)();
8
10
  (0, react_1.useEffect)(() => {
9
11
  return (0, cache_1.subscribe)(environment, encounteredRecords, () => {
10
12
  return setState({});
11
13
  });
12
- // TODO this is probably buggy — we should re-evaluate the effect when
13
- // encounteredRecords changes. However, it is not a stable object, so...
14
- // how?
15
- }, []);
14
+ }, [encounteredRecords]);
16
15
  }
17
16
  exports.useRerenderWhenEncounteredRecordChanges = useRerenderWhenEncounteredRecordChanges;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@isograph/react",
3
- "version": "0.0.0-main-0a7c1aef",
3
+ "version": "0.0.0-main-d319cfe5",
4
4
  "description": "Use Isograph with React",
5
5
  "homepage": "https://isograph.dev",
6
6
  "main": "dist/index.js",
@@ -16,8 +16,8 @@
16
16
  "prepack": "yarn run test && yarn run compile"
17
17
  },
18
18
  "dependencies": {
19
- "@isograph/disposable-types": "0.0.0-main-0a7c1aef",
20
- "@isograph/react-disposable-state": "0.0.0-main-0a7c1aef",
19
+ "@isograph/disposable-types": "0.0.0-main-d319cfe5",
20
+ "@isograph/react-disposable-state": "0.0.0-main-d319cfe5",
21
21
  "react": "^18.2.0"
22
22
  },
23
23
  "devDependencies": {
@@ -1,14 +1,22 @@
1
1
  import { ParentCache } from '@isograph/react-disposable-state';
2
2
  import { RetainedQuery } from './garbageCollection';
3
3
 
4
- type ComponentName = string;
4
+ export type ComponentOrFieldName = string;
5
5
  type StringifiedArgs = string;
6
- type ComponentCache = {
6
+ type ComponentAndEncounteredRecordsCache = {
7
7
  [key: DataId]: {
8
- [key: ComponentName]: { [key: StringifiedArgs]: React.FC<any> };
8
+ [key: ComponentOrFieldName]: {
9
+ [key: StringifiedArgs]: ComponentAndEncounteredRecords;
10
+ };
9
11
  };
10
12
  };
11
13
 
14
+ export type ComponentAndEncounteredRecords = {
15
+ component: React.FC<any>;
16
+ // null if we have never read before
17
+ encounteredRecordsDuringLastRead: Set<DataId> | null;
18
+ };
19
+
12
20
  type CallbackAndRecords = {
13
21
  callback: () => void;
14
22
  records: Set<DataId> | null;
@@ -20,12 +28,14 @@ export type IsographEnvironment = {
20
28
  store: IsographStore;
21
29
  networkFunction: IsographNetworkFunction;
22
30
  missingFieldHandler: MissingFieldHandler | null;
23
- componentCache: ComponentCache;
24
31
  subscriptions: Subscriptions;
25
- suspenseCache: SuspenseCache;
26
32
  retainedQueries: Set<RetainedQuery>;
27
33
  gcBuffer: Array<RetainedQuery>;
28
34
  gcBufferSize: number;
35
+
36
+ // These caches should be moved into the store somehow
37
+ componentAndEncounteredRecordsCache: ComponentAndEncounteredRecordsCache;
38
+ suspenseCache: SuspenseCache;
29
39
  };
30
40
 
31
41
  export type MissingFieldHandler = (
@@ -85,7 +95,7 @@ export function createIsographEnvironment(
85
95
  store,
86
96
  networkFunction,
87
97
  missingFieldHandler: missingFieldHandler ?? null,
88
- componentCache: {},
98
+ componentAndEncounteredRecordsCache: {},
89
99
  subscriptions: new Set(),
90
100
  suspenseCache: {},
91
101
  retainedQueries: new Set(),
@@ -1,6 +1,10 @@
1
1
  import { stableCopy } from './cache';
2
2
  import { RefetchQueryArtifactWrapper } from './entrypoint';
3
- import { IsographEnvironment, DataId } from './IsographEnvironment';
3
+ import {
4
+ IsographEnvironment,
5
+ DataId,
6
+ ComponentAndEncounteredRecords,
7
+ } from './IsographEnvironment';
4
8
  import { readButDoNotEvaluate } from './read';
5
9
  import { ReaderArtifact } from './reader';
6
10
  import { useRerenderWhenEncounteredRecordChanges } from './useRerenderWhenEncounteredRecordChanges';
@@ -12,8 +16,8 @@ export function getOrCreateCachedComponent(
12
16
  readerArtifact: ReaderArtifact<any, any>,
13
17
  variables: { [key: string]: string },
14
18
  resolverRefetchQueries: RefetchQueryArtifactWrapper[],
15
- ) {
16
- const cachedComponentsById = environment.componentCache;
19
+ ): React.FC<any> {
20
+ const cachedComponentsById = environment.componentAndEncounteredRecordsCache;
17
21
  const stringifiedArgs = JSON.stringify(stableCopy(variables));
18
22
  cachedComponentsById[rootId] = cachedComponentsById[rootId] ?? {};
19
23
  const componentsByName = cachedComponentsById[rootId];
@@ -21,7 +25,7 @@ export function getOrCreateCachedComponent(
21
25
  const byArgs = componentsByName[componentName];
22
26
  byArgs[stringifiedArgs] =
23
27
  byArgs[stringifiedArgs] ??
24
- (() => {
28
+ ((): ComponentAndEncounteredRecords => {
25
29
  function Component(additionalRuntimeProps: { [key: string]: any }) {
26
30
  const { item: data, encounteredRecords } = readButDoNotEvaluate(
27
31
  environment,
@@ -32,6 +36,7 @@ export function getOrCreateCachedComponent(
32
36
  variables,
33
37
  nestedRefetchQueries: resolverRefetchQueries,
34
38
  },
39
+ byArgs[stringifiedArgs],
35
40
  );
36
41
 
37
42
  useRerenderWhenEncounteredRecordChanges(
@@ -46,7 +51,11 @@ export function getOrCreateCachedComponent(
46
51
  return readerArtifact.resolver(data, additionalRuntimeProps);
47
52
  }
48
53
  Component.displayName = `${componentName} (id: ${rootId}) @component`;
49
- return Component;
54
+ return {
55
+ component: Component,
56
+ encounteredRecordsDuringLastRead: null,
57
+ };
50
58
  })();
51
- return byArgs[stringifiedArgs];
59
+
60
+ return byArgs[stringifiedArgs].component;
52
61
  }
package/src/read.ts CHANGED
@@ -4,6 +4,7 @@ import { RefetchQueryArtifactWrapper } from './entrypoint';
4
4
  import { FragmentReference } from './FragmentReference';
5
5
  import {
6
6
  assertLink,
7
+ ComponentAndEncounteredRecords,
7
8
  DataId,
8
9
  defaultMissingFieldHandler,
9
10
  IsographEnvironment,
@@ -60,6 +61,7 @@ export function read<TReadFromStore extends Object, TClientFieldValue>(
60
61
  export function readButDoNotEvaluate<TReadFromStore extends Object>(
61
62
  environment: IsographEnvironment,
62
63
  reference: FragmentReference<TReadFromStore, unknown>,
64
+ encounteredRecordCache: ComponentAndEncounteredRecords,
63
65
  ): WithEncounteredRecords<TReadFromStore> {
64
66
  const mutableEncounteredRecords = new Set<DataId>();
65
67
  const response = readData(
@@ -76,13 +78,42 @@ export function readButDoNotEvaluate<TReadFromStore extends Object>(
76
78
  if (response.kind === 'MissingData') {
77
79
  throw onNextChange(environment);
78
80
  } else {
81
+ const encounteredRecords = compareAndUpdateEncounteredRecords(
82
+ encounteredRecordCache,
83
+ mutableEncounteredRecords,
84
+ );
85
+
79
86
  return {
80
- encounteredRecords: mutableEncounteredRecords,
87
+ encounteredRecords,
81
88
  item: response.data,
82
89
  };
83
90
  }
84
91
  }
85
92
 
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
+
86
117
  type ReadDataResult<TReadFromStore> =
87
118
  | {
88
119
  kind: 'Success';
package/src/reader.ts CHANGED
@@ -1,9 +1,13 @@
1
+ import { ComponentOrFieldName } from './IsographEnvironment';
1
2
  import { Arguments } from './util';
2
3
 
3
4
  // TODO this should probably be at least three distinct types, for @component,
4
5
  // non-@component and refetch resolvers
5
6
  export type ReaderArtifact<TReadFromStore extends Object, TClientFieldValue> = {
6
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
+ fieldName: ComponentOrFieldName;
7
11
  readerAst: ReaderAst<TReadFromStore>;
8
12
  resolver: (data: TReadFromStore, runtimeProps: any) => TClientFieldValue;
9
13
  variant: ReaderResolverVariant;
@@ -25,6 +25,7 @@ const artifact: ReaderArtifact<
25
25
  Query__meName__outputType
26
26
  > = {
27
27
  kind: "ReaderArtifact",
28
+ fieldName: "meName",
28
29
  resolver: resolver as any,
29
30
  readerAst,
30
31
  variant: { kind: "Eager" },
@@ -47,6 +47,7 @@ const artifact: ReaderArtifact<
47
47
  Query__meNameSuccessor__outputType
48
48
  > = {
49
49
  kind: "ReaderArtifact",
50
+ fieldName: "meNameSuccessor",
50
51
  resolver: resolver as any,
51
52
  readerAst,
52
53
  variant: { kind: "Eager" },
@@ -30,6 +30,7 @@ const artifact: ReaderArtifact<
30
30
  Query__nodeField__outputType
31
31
  > = {
32
32
  kind: "ReaderArtifact",
33
+ fieldName: "nodeField",
33
34
  resolver: resolver as any,
34
35
  readerAst,
35
36
  variant: { kind: "Eager" },
@@ -2,6 +2,8 @@ import { useEffect, useState } from 'react';
2
2
  import { DataId, IsographEnvironment } from './IsographEnvironment';
3
3
  import { subscribe } from './cache';
4
4
 
5
+ // TODO add unit tests for this. Add integration tests that test
6
+ // behavior when the encounteredRecords underneath a fragment change.
5
7
  export function useRerenderWhenEncounteredRecordChanges(
6
8
  environment: IsographEnvironment,
7
9
  encounteredRecords: Set<DataId>,
@@ -11,8 +13,5 @@ export function useRerenderWhenEncounteredRecordChanges(
11
13
  return subscribe(environment, encounteredRecords, () => {
12
14
  return setState({});
13
15
  });
14
- // TODO this is probably buggy — we should re-evaluate the effect when
15
- // encounteredRecords changes. However, it is not a stable object, so...
16
- // how?
17
- }, []);
16
+ }, [encounteredRecords]);
18
17
  }