@khanacademy/wonder-blocks-data 5.0.1 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/es/index.js +767 -371
  3. package/dist/index.js +1194 -564
  4. package/legacy-docs.md +3 -0
  5. package/package.json +2 -2
  6. package/src/__docs__/_overview_.stories.mdx +18 -0
  7. package/src/__docs__/_overview_graphql.stories.mdx +35 -0
  8. package/src/__docs__/_overview_ssr_.stories.mdx +185 -0
  9. package/src/__docs__/_overview_testing_.stories.mdx +123 -0
  10. package/src/__docs__/exports.clear-shared-cache.stories.mdx +20 -0
  11. package/src/__docs__/exports.data-error.stories.mdx +23 -0
  12. package/src/__docs__/exports.data-errors.stories.mdx +23 -0
  13. package/src/{components/data.md → __docs__/exports.data.stories.mdx} +15 -18
  14. package/src/__docs__/exports.fulfill-all-data-requests.stories.mdx +24 -0
  15. package/src/__docs__/exports.gql-error.stories.mdx +23 -0
  16. package/src/__docs__/exports.gql-errors.stories.mdx +20 -0
  17. package/src/__docs__/exports.gql-router.stories.mdx +29 -0
  18. package/src/__docs__/exports.has-unfulfilled-requests.stories.mdx +20 -0
  19. package/src/{components/intercept-requests.md → __docs__/exports.intercept-requests.stories.mdx} +16 -1
  20. package/src/__docs__/exports.intialize-cache.stories.mdx +29 -0
  21. package/src/__docs__/exports.remove-all-from-cache.stories.mdx +24 -0
  22. package/src/__docs__/exports.remove-from-cache.stories.mdx +25 -0
  23. package/src/__docs__/exports.request-fulfillment.stories.mdx +36 -0
  24. package/src/__docs__/exports.scoped-in-memory-cache.stories.mdx +92 -0
  25. package/src/__docs__/exports.serializable-in-memory-cache.stories.mdx +112 -0
  26. package/src/__docs__/exports.status.stories.mdx +31 -0
  27. package/src/{components/track-data.md → __docs__/exports.track-data.stories.mdx} +15 -0
  28. package/src/__docs__/exports.use-cached-effect.stories.mdx +41 -0
  29. package/src/__docs__/exports.use-gql.stories.mdx +73 -0
  30. package/src/__docs__/exports.use-hydratable-effect.stories.mdx +43 -0
  31. package/src/__docs__/exports.use-server-effect.stories.mdx +50 -0
  32. package/src/__docs__/exports.use-shared-cache.stories.mdx +30 -0
  33. package/src/__docs__/exports.when-client-side.stories.mdx +33 -0
  34. package/src/__docs__/types.cached-response.stories.mdx +29 -0
  35. package/src/__docs__/types.error-options.stories.mdx +21 -0
  36. package/src/__docs__/types.gql-context.stories.mdx +20 -0
  37. package/src/__docs__/types.gql-fetch-fn.stories.mdx +24 -0
  38. package/src/__docs__/types.gql-fetch-options.stories.mdx +24 -0
  39. package/src/__docs__/types.gql-operation-type.stories.mdx +24 -0
  40. package/src/__docs__/types.gql-operation.stories.mdx +67 -0
  41. package/src/__docs__/types.response-cache.stories.mdx +33 -0
  42. package/src/__docs__/types.result.stories.mdx +39 -0
  43. package/src/__docs__/types.scoped-cache.stories.mdx +27 -0
  44. package/src/__docs__/types.valid-cache-data.stories.mdx +23 -0
  45. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +0 -80
  46. package/src/__tests__/generated-snapshot.test.js +0 -24
  47. package/src/components/__tests__/data.test.js +149 -128
  48. package/src/components/data.js +22 -112
  49. package/src/components/intercept-requests.js +1 -1
  50. package/src/hooks/__tests__/__snapshots__/use-shared-cache.test.js.snap +8 -8
  51. package/src/hooks/__tests__/use-cached-effect.test.js +507 -0
  52. package/src/hooks/__tests__/use-gql-router-context.test.js +133 -0
  53. package/src/hooks/__tests__/use-gql.test.js +1 -30
  54. package/src/hooks/__tests__/use-hydratable-effect.test.js +705 -0
  55. package/src/hooks/__tests__/use-server-effect.test.js +90 -11
  56. package/src/hooks/use-cached-effect.js +225 -0
  57. package/src/hooks/use-gql-router-context.js +50 -0
  58. package/src/hooks/use-gql.js +22 -52
  59. package/src/hooks/use-hydratable-effect.js +206 -0
  60. package/src/hooks/use-request-interception.js +20 -23
  61. package/src/hooks/use-server-effect.js +42 -10
  62. package/src/hooks/use-shared-cache.js +13 -11
  63. package/src/index.js +53 -3
  64. package/src/util/__tests__/__snapshots__/serializable-in-memory-cache.test.js.snap +19 -0
  65. package/src/util/__tests__/merge-gql-context.test.js +74 -0
  66. package/src/util/__tests__/request-fulfillment.test.js +23 -42
  67. package/src/util/__tests__/request-tracking.test.js +26 -7
  68. package/src/util/__tests__/result-from-cache-response.test.js +19 -5
  69. package/src/util/__tests__/scoped-in-memory-cache.test.js +6 -85
  70. package/src/util/__tests__/serializable-in-memory-cache.test.js +398 -0
  71. package/src/util/__tests__/ssr-cache.test.js +52 -52
  72. package/src/util/data-error.js +58 -0
  73. package/src/util/get-gql-data-from-response.js +3 -2
  74. package/src/util/gql-error.js +19 -11
  75. package/src/util/merge-gql-context.js +34 -0
  76. package/src/util/request-fulfillment.js +49 -46
  77. package/src/util/request-tracking.js +69 -15
  78. package/src/util/result-from-cache-response.js +12 -16
  79. package/src/util/scoped-in-memory-cache.js +24 -47
  80. package/src/util/serializable-in-memory-cache.js +49 -0
  81. package/src/util/ssr-cache.js +9 -8
  82. package/src/util/status.js +30 -0
  83. package/src/util/types.js +18 -1
  84. package/docs.md +0 -122
@@ -7,14 +7,14 @@ import type {ResponseCache, ValidCacheData} from "./types.js";
7
7
 
8
8
  type TrackerFn = <TData: ValidCacheData>(
9
9
  id: string,
10
- handler: () => Promise<?TData>,
10
+ handler: () => Promise<TData>,
11
11
  hydrate: boolean,
12
12
  ) => void;
13
13
 
14
14
  type RequestCache = {
15
15
  [id: string]: {|
16
- hydrate?: boolean,
17
- handler: () => Promise<?any>,
16
+ hydrate: boolean,
17
+ handler: () => Promise<any>,
18
18
  |},
19
19
  ...
20
20
  };
@@ -55,7 +55,7 @@ export class RequestTracker {
55
55
 
56
56
  constructor(responseCache: ?SsrCache = undefined) {
57
57
  this._responseCache = responseCache || SsrCache.Default;
58
- this._requestFulfillment = new RequestFulfillment(responseCache);
58
+ this._requestFulfillment = new RequestFulfillment();
59
59
  }
60
60
 
61
61
  /**
@@ -66,11 +66,11 @@ export class RequestTracker {
66
66
  */
67
67
  trackDataRequest: <TData: ValidCacheData>(
68
68
  id: string,
69
- handler: () => Promise<?TData>,
69
+ handler: () => Promise<TData>,
70
70
  hydrate: boolean,
71
71
  ) => void = <TData: ValidCacheData>(
72
72
  id: string,
73
- handler: () => Promise<?TData>,
73
+ handler: () => Promise<TData>,
74
74
  hydrate: boolean,
75
75
  ): void => {
76
76
  /**
@@ -114,13 +114,68 @@ export class RequestTracker {
114
114
  fulfillTrackedRequests: () => Promise<ResponseCache> =
115
115
  (): Promise<ResponseCache> => {
116
116
  const promises = [];
117
+ const {cacheData, cacheError} = this._responseCache;
117
118
 
118
119
  for (const requestKey of Object.keys(this._trackedRequests)) {
119
- const promise = this._requestFulfillment.fulfill(
120
- requestKey,
121
- this._trackedRequests[requestKey],
122
- );
123
- promises.push(promise);
120
+ const options = this._trackedRequests[requestKey];
121
+
122
+ try {
123
+ promises.push(
124
+ this._requestFulfillment
125
+ .fulfill(requestKey, {...options})
126
+ .then((result) => {
127
+ switch (result.status) {
128
+ case "success":
129
+ /**
130
+ * Let's cache the data!
131
+ *
132
+ * NOTE: This only caches when we're
133
+ * server side.
134
+ */
135
+ cacheData(
136
+ requestKey,
137
+ result.data,
138
+ options.hydrate,
139
+ );
140
+ break;
141
+
142
+ case "error":
143
+ /**
144
+ * Let's cache the error!
145
+ *
146
+ * NOTE: This only caches when we're
147
+ * server side.
148
+ */
149
+ cacheError(
150
+ requestKey,
151
+ result.error,
152
+ options.hydrate,
153
+ );
154
+ break;
155
+ }
156
+
157
+ // For status === "loading":
158
+ // Could never get here unless we wrote
159
+ // the code wrong. Rather than bloat
160
+ // code with useless error, just ignore.
161
+
162
+ // For status === "aborted":
163
+ // We won't cache this.
164
+ // We don't hydrate aborted requests,
165
+ // so the client would just see them
166
+ // as unfulfilled data.
167
+ return;
168
+ }),
169
+ );
170
+ } catch (e) {
171
+ // This captures if there are problems in the code that
172
+ // begins the requests.
173
+ promises.push(
174
+ Promise.resolve(
175
+ cacheError(requestKey, e, options.hydrate),
176
+ ),
177
+ );
178
+ }
124
179
  }
125
180
 
126
181
  /**
@@ -129,16 +184,15 @@ export class RequestTracker {
129
184
  * We call this now for a simpler API.
130
185
  *
131
186
  * If we reset the tracked calls after all promises resolve, any
132
- * requst tracking done while promises are in flight would be lost.
187
+ * request tracking done while promises are in flight would be lost.
133
188
  *
134
189
  * If we don't reset at all, then we have to expose the `reset` call
135
190
  * for consumers to use, or they'll only ever be able to accumulate
136
191
  * more and more tracked requests, having to fulfill them all every
137
192
  * time.
138
193
  *
139
- * Calling it here means we can have multiple "track -> request" cycles
140
- * in a row and in an easy to reason about manner.
141
- *
194
+ * Calling it here means we can have multiple "track -> request"
195
+ * cycles in a row and in an easy to reason about manner.
142
196
  */
143
197
  this.reset();
144
198
 
@@ -1,4 +1,6 @@
1
1
  // @flow
2
+ import {Status} from "./status.js";
3
+ import {DataError, DataErrors} from "./data-error.js";
2
4
  import type {ValidCacheData, CachedResponse, Result} from "./types.js";
3
5
 
4
6
  /**
@@ -6,30 +8,24 @@ import type {ValidCacheData, CachedResponse, Result} from "./types.js";
6
8
  */
7
9
  export const resultFromCachedResponse = <TData: ValidCacheData>(
8
10
  cacheEntry: ?CachedResponse<TData>,
9
- ): Result<TData> => {
10
- // No cache entry means we didn't load one yet.
11
+ ): ?Result<TData> => {
12
+ // No cache entry means no result to be hydrated.
11
13
  if (cacheEntry == null) {
12
- return {
13
- status: "loading",
14
- };
14
+ return null;
15
15
  }
16
16
 
17
17
  const {data, error} = cacheEntry;
18
18
  if (error != null) {
19
- return {
20
- status: "error",
21
- error,
22
- };
19
+ // Let's hydrate the error. We don't persist everything about the
20
+ // original error on the server, hence why we only superficially
21
+ // hydrate it to a GqlHydratedError.
22
+ return Status.error(new DataError(error, DataErrors.Hydrated));
23
23
  }
24
24
 
25
25
  if (data != null) {
26
- return {
27
- status: "success",
28
- data,
29
- };
26
+ return Status.success(data);
30
27
  }
31
28
 
32
- return {
33
- status: "aborted",
34
- };
29
+ // We shouldn't get here since we don't actually cache null data.
30
+ return Status.aborted();
35
31
  };
@@ -1,6 +1,6 @@
1
1
  // @flow
2
- import {KindError, Errors, clone} from "@khanacademy/wonder-stuff-core";
3
- import type {ValidCacheData, ScopedCache} from "./types.js";
2
+ import {DataError, DataErrors} from "./data-error.js";
3
+ import type {ScopedCache, ValidCacheData} from "./types.js";
4
4
 
5
5
  /**
6
6
  * Describe an in-memory cache.
@@ -8,15 +8,8 @@ import type {ValidCacheData, ScopedCache} from "./types.js";
8
8
  export class ScopedInMemoryCache {
9
9
  _cache: ScopedCache;
10
10
 
11
- constructor(initialCache: ScopedCache = Object.freeze({})) {
12
- try {
13
- this._cache = clone(initialCache);
14
- } catch (e) {
15
- throw new KindError(
16
- `An error occurred trying to initialize from a response cache snapshot: ${e}`,
17
- Errors.InvalidInput,
18
- );
19
- }
11
+ constructor(initialCache: ScopedCache = {}) {
12
+ this._cache = initialCache;
20
13
  }
21
14
 
22
15
  /**
@@ -31,50 +24,47 @@ export class ScopedInMemoryCache {
31
24
  /**
32
25
  * Set a value in the cache.
33
26
  */
34
- set: <TValue: ValidCacheData>(
27
+ set<TValue: ValidCacheData>(
35
28
  scope: string,
36
29
  id: string,
37
30
  value: TValue,
38
- ) => void = <TValue: ValidCacheData>(scope, id, value: TValue): void => {
31
+ ): void {
39
32
  if (!id || typeof id !== "string") {
40
- throw new KindError(
33
+ throw new DataError(
41
34
  "id must be non-empty string",
42
- Errors.InvalidInput,
35
+ DataErrors.InvalidInput,
43
36
  );
44
37
  }
45
38
 
46
39
  if (!scope || typeof scope !== "string") {
47
- throw new KindError(
40
+ throw new DataError(
48
41
  "scope must be non-empty string",
49
- Errors.InvalidInput,
42
+ DataErrors.InvalidInput,
50
43
  );
51
44
  }
52
45
 
53
46
  if (typeof value === "function") {
54
- throw new KindError(
47
+ throw new DataError(
55
48
  "value must be a non-function value",
56
- Errors.InvalidInput,
49
+ DataErrors.InvalidInput,
57
50
  );
58
51
  }
59
52
 
60
53
  this._cache[scope] = this._cache[scope] ?? {};
61
- this._cache[scope][id] = Object.freeze(clone(value));
62
- };
54
+ this._cache[scope][id] = value;
55
+ }
63
56
 
64
57
  /**
65
58
  * Retrieve a value from the cache.
66
59
  */
67
- get: (scope: string, id: string) => ?ValidCacheData = (
68
- scope,
69
- id,
70
- ): ?ValidCacheData => {
60
+ get(scope: string, id: string): ?ValidCacheData {
71
61
  return this._cache[scope]?.[id] ?? null;
72
- };
62
+ }
73
63
 
74
64
  /**
75
65
  * Purge an item from the cache.
76
66
  */
77
- purge: (scope: string, id: string) => void = (scope, id) => {
67
+ purge(scope: string, id: string): void {
78
68
  if (!this._cache[scope]?.[id]) {
79
69
  return;
80
70
  }
@@ -82,17 +72,17 @@ export class ScopedInMemoryCache {
82
72
  if (Object.keys(this._cache[scope]).length === 0) {
83
73
  delete this._cache[scope];
84
74
  }
85
- };
75
+ }
86
76
 
87
77
  /**
88
78
  * Purge a scope of items that match the given predicate.
89
79
  *
90
80
  * If the predicate is omitted, then all items in the scope are purged.
91
81
  */
92
- purgeScope: (
82
+ purgeScope(
93
83
  scope: string,
94
84
  predicate?: (id: string, value: ValidCacheData) => boolean,
95
- ) => void = (scope, predicate) => {
85
+ ): void {
96
86
  if (!this._cache[scope]) {
97
87
  return;
98
88
  }
@@ -110,20 +100,20 @@ export class ScopedInMemoryCache {
110
100
  if (Object.keys(this._cache[scope]).length === 0) {
111
101
  delete this._cache[scope];
112
102
  }
113
- };
103
+ }
114
104
 
115
105
  /**
116
106
  * Purge all items from the cache that match the given predicate.
117
107
  *
118
108
  * If the predicate is omitted, then all items in the cache are purged.
119
109
  */
120
- purgeAll: (
110
+ purgeAll(
121
111
  predicate?: (
122
112
  scope: string,
123
113
  id: string,
124
114
  value: ValidCacheData,
125
115
  ) => boolean,
126
- ) => void = (predicate) => {
116
+ ): void {
127
117
  if (predicate == null) {
128
118
  this._cache = {};
129
119
  return;
@@ -132,18 +122,5 @@ export class ScopedInMemoryCache {
132
122
  for (const scope of Object.keys(this._cache)) {
133
123
  this.purgeScope(scope, (id, value) => predicate(scope, id, value));
134
124
  }
135
- };
136
-
137
- /**
138
- * Clone the cache.
139
- */
140
- clone: () => ScopedCache = () => {
141
- try {
142
- return clone(this._cache);
143
- } catch (e) {
144
- throw new Error(
145
- `An error occurred while trying to clone the cache: ${e}`,
146
- );
147
- }
148
- };
125
+ }
149
126
  }
@@ -0,0 +1,49 @@
1
+ // @flow
2
+ import {clone} from "@khanacademy/wonder-stuff-core";
3
+ import {DataError, DataErrors} from "./data-error.js";
4
+ import {ScopedInMemoryCache} from "./scoped-in-memory-cache.js";
5
+ import type {ValidCacheData, ScopedCache} from "./types.js";
6
+
7
+ /**
8
+ * Describe a serializable in-memory cache.
9
+ */
10
+ export class SerializableInMemoryCache extends ScopedInMemoryCache {
11
+ constructor(initialCache: ScopedCache = {}) {
12
+ try {
13
+ super(clone(initialCache));
14
+ } catch (e) {
15
+ throw new DataError(
16
+ `An error occurred trying to initialize from a response cache snapshot: ${e}`,
17
+ DataErrors.InvalidInput,
18
+ );
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Set a value in the cache.
24
+ */
25
+ set<TValue: ValidCacheData>(
26
+ scope: string,
27
+ id: string,
28
+ value: TValue,
29
+ ): void {
30
+ super.set(scope, id, Object.freeze(clone(value)));
31
+ }
32
+
33
+ /**
34
+ * Clone the cache.
35
+ */
36
+ clone(): ScopedCache {
37
+ try {
38
+ return clone(this._cache);
39
+ } catch (e) {
40
+ throw new DataError(
41
+ "An error occurred while trying to clone the cache",
42
+ DataErrors.Internal,
43
+ {
44
+ cause: e,
45
+ },
46
+ );
47
+ }
48
+ }
49
+ }
@@ -1,6 +1,6 @@
1
1
  // @flow
2
2
  import {Server} from "@khanacademy/wonder-blocks-core";
3
- import {ScopedInMemoryCache} from "./scoped-in-memory-cache.js";
3
+ import {SerializableInMemoryCache} from "./serializable-in-memory-cache.js";
4
4
 
5
5
  import type {ValidCacheData, CachedResponse, ResponseCache} from "./types.js";
6
6
 
@@ -25,17 +25,18 @@ export class SsrCache {
25
25
  return _default;
26
26
  }
27
27
 
28
- _hydrationCache: ScopedInMemoryCache;
29
- _ssrOnlyCache: ?ScopedInMemoryCache;
28
+ _hydrationCache: SerializableInMemoryCache;
29
+ _ssrOnlyCache: ?SerializableInMemoryCache;
30
30
 
31
31
  constructor(
32
- hydrationCache: ?ScopedInMemoryCache = null,
33
- ssrOnlyCache: ?ScopedInMemoryCache = null,
32
+ hydrationCache: ?SerializableInMemoryCache = null,
33
+ ssrOnlyCache: ?SerializableInMemoryCache = null,
34
34
  ) {
35
35
  this._ssrOnlyCache = Server.isServerSide()
36
- ? ssrOnlyCache || new ScopedInMemoryCache()
36
+ ? ssrOnlyCache || new SerializableInMemoryCache()
37
37
  : undefined;
38
- this._hydrationCache = hydrationCache || new ScopedInMemoryCache();
38
+ this._hydrationCache =
39
+ hydrationCache || new SerializableInMemoryCache();
39
40
  }
40
41
 
41
42
  _setCachedResponse<TData: ValidCacheData>(
@@ -70,7 +71,7 @@ export class SsrCache {
70
71
  "Cannot initialize data response cache more than once",
71
72
  );
72
73
  }
73
- this._hydrationCache = new ScopedInMemoryCache({
74
+ this._hydrationCache = new SerializableInMemoryCache({
74
75
  // $FlowIgnore[incompatible-call]
75
76
  [DefaultScope]: source,
76
77
  });
@@ -0,0 +1,30 @@
1
+ // @flow
2
+ import type {Result, ValidCacheData} from "./types.js";
3
+
4
+ const loadingStatus = Object.freeze({
5
+ status: "loading",
6
+ });
7
+
8
+ const abortedStatus = Object.freeze({
9
+ status: "aborted",
10
+ });
11
+
12
+ /**
13
+ * Create Result<TData> instances with specific statuses.
14
+ */
15
+ export const Status = Object.freeze({
16
+ loading: <TData: ValidCacheData = ValidCacheData>(): Result<TData> =>
17
+ loadingStatus,
18
+ aborted: <TData: ValidCacheData = ValidCacheData>(): Result<TData> =>
19
+ abortedStatus,
20
+ success: <TData: ValidCacheData>(data: TData): Result<TData> => ({
21
+ status: "success",
22
+ data,
23
+ }),
24
+ error: <TData: ValidCacheData = ValidCacheData>(
25
+ error: Error,
26
+ ): Result<TData> => ({
27
+ status: "error",
28
+ error,
29
+ }),
30
+ });
package/src/util/types.js CHANGED
@@ -1,4 +1,6 @@
1
1
  // @flow
2
+ import type {Metadata} from "@khanacademy/wonder-stuff-core";
3
+
2
4
  /**
3
5
  * Define what can be cached.
4
6
  *
@@ -25,7 +27,7 @@ export type Result<TData: ValidCacheData> =
25
27
  |}
26
28
  | {|
27
29
  status: "error",
28
- error: string,
30
+ error: Error,
29
31
  |}
30
32
  | {|
31
33
  status: "aborted",
@@ -68,3 +70,18 @@ export type ScopedCache = {
68
70
  },
69
71
  ...
70
72
  };
73
+
74
+ /**
75
+ * Options to pass to error construction.
76
+ */
77
+ export type ErrorOptions = {|
78
+ /**
79
+ * Metadata to attach to the error.
80
+ */
81
+ metadata?: ?Metadata,
82
+
83
+ /**
84
+ * The error that caused the error being constructed.
85
+ */
86
+ cause?: ?Error,
87
+ |};
package/docs.md DELETED
@@ -1,122 +0,0 @@
1
- ## fulfillAllDataRequests
2
-
3
- When performing server-side rendering (SSR), the data requests that are being
4
- made via the `Data` component can be tracked by rendering the React tree
5
- inside the `TrackData` component. After this has occurred, the tracked requests
6
- can be fulfilled using `fulfillAllDataRequests`.
7
-
8
- This method returns a promise that resolves to a copy of the data that was
9
- cached by fulfilling the tracked requests. In the process, it clears the
10
- record of tracked requests so that new requests can be tracked and fulfilled
11
- if so required.
12
-
13
- The returned copy of the data cache can be used with the `initializeCache`
14
- method to prepare the data cache before a subsequent render. This is useful on
15
- the server to then SSR a more complete result, and again on the client, to
16
- rehydrate that result.
17
-
18
- ### Usage
19
-
20
- ```js static
21
- fulfillAllDataRequests(): Promise<ResponseCache>;
22
- ```
23
-
24
- ## initializeCache
25
-
26
- Wonder Blocks Data caches data in its response cache for hydration. This cache
27
- can be initialized with data using the `initializeCache` method.
28
- The `initializeCache` method can only be called when the hydration cache is
29
- empty.
30
-
31
- Usually, the data to be passed to `initializeCache` will be obtained by
32
- calling `fulfillAllDataRequests` after tracking data requests
33
- (see [TrackData](#trackdata)) during server-side rendering.
34
-
35
- Combine with `removeFromCache` or `removeAllFromCache` to support your testing
36
- needs.
37
-
38
- ### Usage
39
-
40
- ```js static
41
- initializeCache(sourceCache: ResponseCache): void;
42
- ```
43
-
44
- #### Function arguments
45
-
46
- | Argument | Flow&nbsp;Type | Default | Description |
47
- | --- | --- | --- | --- |
48
- | `sourceData` | `ResponseCache` | _Required_ | The source cache that will be used to initialize the response cache. |
49
-
50
- ## removeFromCache
51
-
52
- Removes an entry from the cache. The given handler and options identify the entry to be removed.
53
-
54
- If an item is removed, this returns `true`; otherwise, `false`.
55
-
56
- This can be used after `initializeCache` to manipulate the cache prior to hydration.
57
- This can be useful during testing.
58
-
59
- ### Usage
60
-
61
- ```js static
62
- removeFromCache(id: string): boolean;
63
- ```
64
-
65
- #### Function arguments
66
-
67
- | Argument | Flow&nbsp;Type | Default | Description |
68
- | --- | --- | --- | --- |
69
- | `id` | `string` | _Required_ | The id of the item to be removed. |
70
-
71
- ## removeAllFromCache
72
-
73
- Removes all entries that match a given predicate from the cache. If no predicate is given, all cached entries for the given handler are removed.
74
-
75
- This returns the count of entries removed.
76
-
77
- This can be used after `initializeCache` to manipulate the cache prior to hydration.
78
- This can be useful during testing (especially to clear the cache so that it can be initialized again).
79
- If the predicate is not given, all items are removed.
80
-
81
- ### Usage
82
-
83
- ```js static
84
- removeAllFromCache(predicate?: (key: string, entry: $ReadOnly<CachedResponse<TData>>) => boolean): number;
85
- ```
86
-
87
- #### Function arguments
88
-
89
- | Argument | Flow&nbsp;Type | Default | Description |
90
- | --- | --- | --- | --- |
91
- | `predicate` | `(key: string, entry: $ReadOnly<CachedResponse<TData>>) => boolean)` | _Optional_ | A predicate to identify which entries to remove. If absent, all data is removed; if present, any entries for which the predicate returns `true` will be returned. |
92
-
93
- ## Types
94
-
95
- ### ResponseCache
96
-
97
- ```js static
98
- type CachedResponse =
99
- | {|
100
- data: any,
101
- |}
102
- | {|
103
- error: string,
104
- |};
105
-
106
- type ResponseCache = {
107
- [id: string]: CachedResponse,
108
- ...,
109
- };
110
- ```
111
-
112
- An example is of the response cache is shown below.
113
-
114
- ```js static
115
- const responseCache = {
116
- DATA_ID_1: {error: "It go 💥boom 😢"},
117
- DATA_ID_2: {data: ["array", "of", "data"]},
118
- DATA_ID_3: {data: {some: "data"}},
119
- };
120
- ```
121
-
122
- In this example, the cache contains data retrieved for three different requests.