@khanacademy/wonder-blocks-data 8.0.3 → 9.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.
- package/CHANGELOG.md +19 -0
- package/dist/es/index.js +70 -78
- package/dist/index.js +130 -147
- package/package.json +2 -2
- package/src/__docs__/_overview_ssr_.stories.mdx +2 -2
- package/src/__docs__/exports.purge-caches.stories.mdx +1 -1
- package/src/__docs__/exports.scoped-in-memory-cache.stories.mdx +3 -3
- package/src/__docs__/exports.serializable-in-memory-cache.stories.mdx +2 -2
- package/src/__docs__/exports.shared-cache.stories.mdx +16 -0
- package/src/__docs__/exports.use-shared-cache.stories.mdx +2 -2
- package/src/__docs__/types.raw-scoped-cache.stories.mdx +27 -0
- package/src/__docs__/types.scoped-cache.stories.mdx +96 -9
- package/src/components/__tests__/data.test.js +2 -2
- package/src/hooks/__tests__/use-shared-cache.test.js +2 -50
- package/src/hooks/use-cached-effect.js +3 -14
- package/src/hooks/use-hydratable-effect.js +0 -4
- package/src/hooks/use-shared-cache.js +6 -14
- package/src/index.js +4 -2
- package/src/util/__tests__/purge-caches.test.js +2 -2
- package/src/util/purge-caches.js +2 -2
- package/src/util/scoped-in-memory-cache.js +5 -9
- package/src/util/serializable-in-memory-cache.js +4 -8
- package/src/util/types.js +38 -5
- package/src/__docs__/exports.purge-shared-cache.stories.mdx +0 -20
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {Meta} from "@storybook/addon-docs";
|
|
2
|
+
|
|
3
|
+
<Meta
|
|
4
|
+
title="Data / Types / RawScopedCache"
|
|
5
|
+
parameters={{
|
|
6
|
+
chromatic: {
|
|
7
|
+
disableSnapshot: true,
|
|
8
|
+
},
|
|
9
|
+
}}
|
|
10
|
+
/>
|
|
11
|
+
|
|
12
|
+
# RawScopedCache
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
type RawScopedCache = {
|
|
16
|
+
[scope: string]: {
|
|
17
|
+
[id: string]: ValidCacheData,
|
|
18
|
+
...
|
|
19
|
+
},
|
|
20
|
+
...
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`RawScopedCache` describes a cache that has distinct scoped sections in its raw object form. This is the representation of the caches used internally by Wonder Blocks Data to support the scoping of requests when using hooks such as [`useSharedCache`](/docs/data-exports-use-shared-cache--page), [`useCachedEffect`](/docs/data-exports-use-cached-effect--page), and [`useHydratableEffect`](/docs/data-exports-use-hydratable-effect--page).
|
|
26
|
+
|
|
27
|
+
See the section on [server-side rendering](/docs/data-server-side-rendering-and-hydration--page) for more information.
|
|
@@ -12,16 +12,103 @@ import {Meta} from "@storybook/addon-docs";
|
|
|
12
12
|
# ScopedCache
|
|
13
13
|
|
|
14
14
|
```ts
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
[id: string]: ValidCacheData,
|
|
18
|
-
...
|
|
19
|
-
},
|
|
20
|
-
...
|
|
21
|
-
};
|
|
15
|
+
interface ScopedCache {
|
|
16
|
+
set(scope: string, id: string, value: ValidCacheData): void;
|
|
22
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Retrieve a value from the cache.
|
|
20
|
+
*/
|
|
21
|
+
get(scope: string, id: string): ?ValidCacheData;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Purge an item from the cache.
|
|
25
|
+
*/
|
|
26
|
+
purge(scope: string, id: string): void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Purge a scope of items that match the given predicate.
|
|
30
|
+
*
|
|
31
|
+
* If the predicate is omitted, then all items in the scope are purged.
|
|
32
|
+
*/
|
|
33
|
+
purgeScope(
|
|
34
|
+
scope: string,
|
|
35
|
+
predicate?: (id: string, value: ValidCacheData) => boolean,
|
|
36
|
+
): void;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Purge all items from the cache that match the given predicate.
|
|
40
|
+
*
|
|
41
|
+
* If the predicate is omitted, then all items in the cache are purged.
|
|
42
|
+
*/
|
|
43
|
+
purgeAll(
|
|
44
|
+
predicate?: (
|
|
45
|
+
scope: string,
|
|
46
|
+
id: string,
|
|
47
|
+
value: ValidCacheData,
|
|
48
|
+
) => boolean,
|
|
49
|
+
): void;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This interface defines how to interact with a scoped cache, such as [`ScopedInMemoryCache`](/docs/data-exports-scopedinmemorycache--page).
|
|
54
|
+
|
|
55
|
+
## set()
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
set(
|
|
59
|
+
scope: string,
|
|
60
|
+
id: string,
|
|
61
|
+
value: TValue,
|
|
62
|
+
): void;
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Sets a value in the cache within a given scope.
|
|
66
|
+
|
|
67
|
+
### Throws
|
|
68
|
+
|
|
69
|
+
| Error Type | Error Name | Reason |
|
|
70
|
+
| ------ | ------ | ------ |
|
|
71
|
+
| [`DataError`](/docs/data-exports-dataerror--page) | `InvalidInputDataError` | `id` and `scope` must be non-empty strings |
|
|
72
|
+
| [`DataError`](/docs/data-exports-dataerror--page) | `InvalidInputDataError` | `value` must be a non-function value |
|
|
73
|
+
|
|
74
|
+
## get()
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
get(scope: string, id: string): ?ValidCacheData;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Gets a value from the cache. If a value with the given identifier (`id`) is not found within the given scope (`scope`) of the cache, `null` is returned.
|
|
81
|
+
|
|
82
|
+
## purge()
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
purge(scope: string, id: string): void;
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Purges the value from the cache. If a value with the given identifier (`id`) is not found within the given scope (`scope`) of the cache, nothing happens.
|
|
89
|
+
|
|
90
|
+
## purgeScope()
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
purgeScope(
|
|
94
|
+
scope: string,
|
|
95
|
+
predicate?: (id: string, value: ValidCacheData) => boolean,
|
|
96
|
+
): void;
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Purges items within a given scope (`scope`) of the cache from that scope. If a predicate is provided, only items for which the predicate returns `true` will be purged; otherwise, the entire scope will be purged.
|
|
100
|
+
|
|
101
|
+
## purgeAll()
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
purgeAll(
|
|
105
|
+
predicate?: (
|
|
106
|
+
scope: string,
|
|
107
|
+
id: string,
|
|
108
|
+
value: ValidCacheData,
|
|
109
|
+
) => boolean,
|
|
110
|
+
): void;
|
|
23
111
|
```
|
|
24
112
|
|
|
25
|
-
|
|
113
|
+
Purges all items from the cache. If a predicate is provided, only items for which the predicate returns `true` will be purged; otherwise, the entire cache will be purged.
|
|
26
114
|
|
|
27
|
-
See the section on [server-side rendering](/docs/data-server-side-rendering-and-hydration--page) for more information.
|
|
@@ -7,7 +7,7 @@ import {render, act} from "@testing-library/react";
|
|
|
7
7
|
import * as ReactDOMServer from "react-dom/server";
|
|
8
8
|
import {Server, View} from "@khanacademy/wonder-blocks-core";
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import {SharedCache} from "../../hooks/use-shared-cache.js";
|
|
11
11
|
import TrackData from "../track-data.js";
|
|
12
12
|
import {RequestFulfillment} from "../../util/request-fulfillment.js";
|
|
13
13
|
import {SsrCache} from "../../util/ssr-cache.js";
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
|
|
25
25
|
describe("Data", () => {
|
|
26
26
|
beforeEach(() => {
|
|
27
|
-
|
|
27
|
+
SharedCache.purgeAll();
|
|
28
28
|
|
|
29
29
|
const responseCache = new SsrCache();
|
|
30
30
|
jest.spyOn(SsrCache, "Default", "get").mockReturnValue(responseCache);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import {renderHook as clientRenderHook} from "@testing-library/react-hooks";
|
|
3
3
|
|
|
4
|
-
import {useSharedCache,
|
|
4
|
+
import {useSharedCache, SharedCache} from "../use-shared-cache.js";
|
|
5
5
|
|
|
6
6
|
describe("#useSharedCache", () => {
|
|
7
7
|
beforeEach(() => {
|
|
8
|
-
|
|
8
|
+
SharedCache.purgeAll();
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
it.each`
|
|
@@ -257,51 +257,3 @@ describe("#useSharedCache", () => {
|
|
|
257
257
|
expect(result).toBeNull();
|
|
258
258
|
});
|
|
259
259
|
});
|
|
260
|
-
|
|
261
|
-
describe("#purgeSharedCache", () => {
|
|
262
|
-
beforeEach(() => {
|
|
263
|
-
purgeSharedCache();
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it("should clear the entire cache if no scope given", () => {
|
|
267
|
-
// Arrange
|
|
268
|
-
const hook1 = clientRenderHook(() => useSharedCache("id1", "scope1"));
|
|
269
|
-
const hook2 = clientRenderHook(() => useSharedCache("id2", "scope2"));
|
|
270
|
-
hook1.result.current[1]("VALUE_1");
|
|
271
|
-
hook2.result.current[1]("VALUE_2");
|
|
272
|
-
// Make sure both hook results include the updated value.
|
|
273
|
-
hook1.rerender();
|
|
274
|
-
hook2.rerender();
|
|
275
|
-
|
|
276
|
-
// Act
|
|
277
|
-
purgeSharedCache();
|
|
278
|
-
// Make sure we refresh the hook results.
|
|
279
|
-
hook1.rerender();
|
|
280
|
-
hook2.rerender();
|
|
281
|
-
|
|
282
|
-
// Assert
|
|
283
|
-
expect(hook1.result.current[0]).toBeNull();
|
|
284
|
-
expect(hook2.result.current[0]).toBeNull();
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it("should clear the given scope only", () => {
|
|
288
|
-
// Arrange
|
|
289
|
-
const hook1 = clientRenderHook(() => useSharedCache("id1", "scope1"));
|
|
290
|
-
const hook2 = clientRenderHook(() => useSharedCache("id2", "scope2"));
|
|
291
|
-
hook1.result.current[1]("VALUE_1");
|
|
292
|
-
hook2.result.current[1]("VALUE_2");
|
|
293
|
-
// Make sure both hook results include the updated value.
|
|
294
|
-
hook1.rerender();
|
|
295
|
-
hook2.rerender();
|
|
296
|
-
|
|
297
|
-
// Act
|
|
298
|
-
purgeSharedCache("scope2");
|
|
299
|
-
// Make sure we refresh the hook results.
|
|
300
|
-
hook1.rerender();
|
|
301
|
-
hook2.rerender();
|
|
302
|
-
|
|
303
|
-
// Assert
|
|
304
|
-
expect(hook1.result.current[0]).toBe("VALUE_1");
|
|
305
|
-
expect(hook2.result.current[0]).toBeNull();
|
|
306
|
-
});
|
|
307
|
-
});
|
|
@@ -232,10 +232,6 @@ export const useCachedEffect = <TData: ValidCacheData>(
|
|
|
232
232
|
fetchPolicy,
|
|
233
233
|
]);
|
|
234
234
|
|
|
235
|
-
// We need to trigger a re-render when the request ID changes as that
|
|
236
|
-
// indicates its a different request.
|
|
237
|
-
const requestIdRef = React.useRef(requestId);
|
|
238
|
-
|
|
239
235
|
// Calculate if we want to fetch the result or not.
|
|
240
236
|
// If this is true, we will do a new fetch, cancelling the previous fetch
|
|
241
237
|
// if there is one inflight.
|
|
@@ -252,12 +248,8 @@ export const useCachedEffect = <TData: ValidCacheData>(
|
|
|
252
248
|
return false;
|
|
253
249
|
|
|
254
250
|
case FetchPolicy.CacheBeforeNetwork:
|
|
255
|
-
// If we don't have a cached value
|
|
256
|
-
|
|
257
|
-
return (
|
|
258
|
-
mostRecentResult == null ||
|
|
259
|
-
requestId !== requestIdRef.current
|
|
260
|
-
);
|
|
251
|
+
// If we don't have a cached value then we need to fetch.
|
|
252
|
+
return mostRecentResult == null;
|
|
261
253
|
|
|
262
254
|
case FetchPolicy.CacheAndNetwork:
|
|
263
255
|
case FetchPolicy.NetworkOnly:
|
|
@@ -265,10 +257,7 @@ export const useCachedEffect = <TData: ValidCacheData>(
|
|
|
265
257
|
// result, then we need to fetch one.
|
|
266
258
|
return networkResultRef.current == null;
|
|
267
259
|
}
|
|
268
|
-
}, [
|
|
269
|
-
|
|
270
|
-
// Let's make sure our ref is set to the most recent requestId.
|
|
271
|
-
requestIdRef.current = requestId;
|
|
260
|
+
}, [mostRecentResult, fetchPolicy, hardSkip]);
|
|
272
261
|
|
|
273
262
|
React.useEffect(() => {
|
|
274
263
|
if (!shouldFetch) {
|
|
@@ -16,9 +16,6 @@ import type {Result, ValidCacheData} from "../util/types.js";
|
|
|
16
16
|
* Policies to define how a hydratable effect should behave client-side.
|
|
17
17
|
*/
|
|
18
18
|
export enum WhenClientSide {
|
|
19
|
-
// TODO(somewhatabstract, FEI-4172): Update eslint-plugin-flowtype when
|
|
20
|
-
// they've fixed https://github.com/gajus/eslint-plugin-flowtype/issues/502
|
|
21
|
-
/* eslint-disable no-undef */
|
|
22
19
|
/**
|
|
23
20
|
* The result from executing the effect server-side will not be hydrated.
|
|
24
21
|
* The effect will always be executed client-side.
|
|
@@ -52,7 +49,6 @@ export enum WhenClientSide {
|
|
|
52
49
|
* hydrated result status.
|
|
53
50
|
*/
|
|
54
51
|
AlwaysExecute,
|
|
55
|
-
/* eslint-enable no-undef */
|
|
56
52
|
}
|
|
57
53
|
|
|
58
54
|
type HydratableEffectOptions<TData: ValidCacheData> = {|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import {DataError, DataErrors} from "../util/data-error.js";
|
|
4
4
|
import {ScopedInMemoryCache} from "../util/scoped-in-memory-cache.js";
|
|
5
|
-
import type {ValidCacheData} from "../util/types.js";
|
|
5
|
+
import type {ValidCacheData, ScopedCache} from "../util/types.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* A function for inserting a value into the cache or clearing it.
|
|
@@ -17,17 +17,12 @@ type CacheValueFn<TValue: ValidCacheData> = (value: ?TValue) => void;
|
|
|
17
17
|
const cache = new ScopedInMemoryCache();
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* Access to the shared in-memory cache.
|
|
21
|
+
*
|
|
22
|
+
* This is the cache used by `useSharedCache` and related hooks and
|
|
23
|
+
* components.
|
|
21
24
|
*/
|
|
22
|
-
export const
|
|
23
|
-
// If we have a valid scope (empty string is falsy), then clear that scope.
|
|
24
|
-
if (scope && typeof scope === "string") {
|
|
25
|
-
cache.purgeScope(scope);
|
|
26
|
-
} else {
|
|
27
|
-
// Just reset the object. This should be sufficient.
|
|
28
|
-
cache.purgeAll();
|
|
29
|
-
}
|
|
30
|
-
};
|
|
25
|
+
export const SharedCache: ScopedCache = cache;
|
|
31
26
|
|
|
32
27
|
/**
|
|
33
28
|
* Hook to retrieve data from and store data in an in-memory cache.
|
|
@@ -37,9 +32,6 @@ export const purgeSharedCache = (scope: string = "") => {
|
|
|
37
32
|
* function to set the cache entry (passing null or undefined to this function
|
|
38
33
|
* will delete the entry).
|
|
39
34
|
*
|
|
40
|
-
* To clear a single scope within the cache or the entire cache,
|
|
41
|
-
* the `clearScopedCache` export is available.
|
|
42
|
-
*
|
|
43
35
|
* NOTE: Unlike useState or useReducer, we don't automatically update folks
|
|
44
36
|
* if the value they reference changes. We might add it later (if we need to),
|
|
45
37
|
* but the likelihood here is that things won't be changing in this cache in a
|
package/src/index.js
CHANGED
|
@@ -9,8 +9,9 @@ export type {
|
|
|
9
9
|
ResponseCache,
|
|
10
10
|
CachedResponse,
|
|
11
11
|
Result,
|
|
12
|
-
|
|
12
|
+
RawScopedCache,
|
|
13
13
|
ValidCacheData,
|
|
14
|
+
ScopedCache,
|
|
14
15
|
} from "./util/types.js";
|
|
15
16
|
|
|
16
17
|
export * from "./util/hydration-cache-api.js";
|
|
@@ -22,7 +23,7 @@ export {default as InterceptRequests} from "./components/intercept-requests.js";
|
|
|
22
23
|
export {DataError, DataErrors} from "./util/data-error.js";
|
|
23
24
|
export {useServerEffect} from "./hooks/use-server-effect.js";
|
|
24
25
|
export {useCachedEffect} from "./hooks/use-cached-effect.js";
|
|
25
|
-
export {useSharedCache,
|
|
26
|
+
export {useSharedCache, SharedCache} from "./hooks/use-shared-cache.js";
|
|
26
27
|
export {
|
|
27
28
|
useHydratableEffect,
|
|
28
29
|
// TODO(somewhatabstract, FEI-4174): Update eslint-plugin-import when they
|
|
@@ -39,6 +40,7 @@ export {Status} from "./util/status.js";
|
|
|
39
40
|
// GraphQL
|
|
40
41
|
////////////////////////////////////////////////////////////////////////////////
|
|
41
42
|
export {getGqlRequestId} from "./util/get-gql-request-id.js";
|
|
43
|
+
export {getGqlDataFromResponse} from "./util/get-gql-data-from-response.js";
|
|
42
44
|
export {graphQLDocumentNodeParser} from "./util/graphql-document-node-parser.js";
|
|
43
45
|
export {toGqlOperation} from "./util/to-gql-operation.js";
|
|
44
46
|
export {GqlRouter} from "./components/gql-router.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import
|
|
2
|
+
import {SharedCache} from "../../hooks/use-shared-cache.js";
|
|
3
3
|
import * as HydrationCacheApi from "../hydration-cache-api.js";
|
|
4
4
|
|
|
5
5
|
import {purgeCaches} from "../purge-caches.js";
|
|
@@ -7,7 +7,7 @@ import {purgeCaches} from "../purge-caches.js";
|
|
|
7
7
|
describe("#purgeCaches", () => {
|
|
8
8
|
it("should purge the shared cache", () => {
|
|
9
9
|
// Arrange
|
|
10
|
-
const spy = jest.spyOn(
|
|
10
|
+
const spy = jest.spyOn(SharedCache, "purgeAll");
|
|
11
11
|
|
|
12
12
|
// Act
|
|
13
13
|
purgeCaches();
|
package/src/util/purge-caches.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @flow
|
|
2
|
-
import {
|
|
2
|
+
import {SharedCache} from "../hooks/use-shared-cache.js";
|
|
3
3
|
import {purgeHydrationCache} from "./hydration-cache-api.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -10,6 +10,6 @@ import {purgeHydrationCache} from "./hydration-cache-api.js";
|
|
|
10
10
|
* which caches may have been used during a given test run.
|
|
11
11
|
*/
|
|
12
12
|
export const purgeCaches = () => {
|
|
13
|
-
|
|
13
|
+
SharedCache.purgeAll();
|
|
14
14
|
purgeHydrationCache();
|
|
15
15
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import {DataError, DataErrors} from "./data-error.js";
|
|
3
|
-
import type {ScopedCache, ValidCacheData} from "./types.js";
|
|
3
|
+
import type {ScopedCache, RawScopedCache, ValidCacheData} from "./types.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Describe an in-memory cache.
|
|
7
7
|
*/
|
|
8
|
-
export class ScopedInMemoryCache {
|
|
9
|
-
_cache:
|
|
8
|
+
export class ScopedInMemoryCache implements ScopedCache {
|
|
9
|
+
_cache: RawScopedCache;
|
|
10
10
|
|
|
11
|
-
constructor(initialCache:
|
|
11
|
+
constructor(initialCache: RawScopedCache = {}) {
|
|
12
12
|
this._cache = initialCache;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -24,11 +24,7 @@ export class ScopedInMemoryCache {
|
|
|
24
24
|
/**
|
|
25
25
|
* Set a value in the cache.
|
|
26
26
|
*/
|
|
27
|
-
set
|
|
28
|
-
scope: string,
|
|
29
|
-
id: string,
|
|
30
|
-
value: TValue,
|
|
31
|
-
): void {
|
|
27
|
+
set(scope: string, id: string, value: ValidCacheData): void {
|
|
32
28
|
if (!id || typeof id !== "string") {
|
|
33
29
|
throw new DataError(
|
|
34
30
|
"id must be non-empty string",
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
import {clone} from "@khanacademy/wonder-stuff-core";
|
|
3
3
|
import {DataError, DataErrors} from "./data-error.js";
|
|
4
4
|
import {ScopedInMemoryCache} from "./scoped-in-memory-cache.js";
|
|
5
|
-
import type {ValidCacheData,
|
|
5
|
+
import type {ValidCacheData, RawScopedCache} from "./types.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Describe a serializable in-memory cache.
|
|
9
9
|
*/
|
|
10
10
|
export class SerializableInMemoryCache extends ScopedInMemoryCache {
|
|
11
|
-
constructor(initialCache:
|
|
11
|
+
constructor(initialCache: RawScopedCache = {}) {
|
|
12
12
|
try {
|
|
13
13
|
super(clone(initialCache));
|
|
14
14
|
} catch (e) {
|
|
@@ -22,18 +22,14 @@ export class SerializableInMemoryCache extends ScopedInMemoryCache {
|
|
|
22
22
|
/**
|
|
23
23
|
* Set a value in the cache.
|
|
24
24
|
*/
|
|
25
|
-
set
|
|
26
|
-
scope: string,
|
|
27
|
-
id: string,
|
|
28
|
-
value: TValue,
|
|
29
|
-
): void {
|
|
25
|
+
set(scope: string, id: string, value: ValidCacheData): void {
|
|
30
26
|
super.set(scope, id, Object.freeze(clone(value)));
|
|
31
27
|
}
|
|
32
28
|
|
|
33
29
|
/**
|
|
34
30
|
* Clone the cache.
|
|
35
31
|
*/
|
|
36
|
-
clone():
|
|
32
|
+
clone(): RawScopedCache {
|
|
37
33
|
try {
|
|
38
34
|
return clone(this._cache);
|
|
39
35
|
} catch (e) {
|
package/src/util/types.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import type {Metadata} from "@khanacademy/wonder-stuff-core";
|
|
3
3
|
|
|
4
|
-
// TODO(somewhatabstract, FEI-4172): Update eslint-plugin-flowtype when
|
|
5
|
-
// they've fixed https://github.com/gajus/eslint-plugin-flowtype/issues/502
|
|
6
|
-
/* eslint-disable no-undef */
|
|
7
4
|
/**
|
|
8
5
|
* Defines the various fetch policies that can be applied to requests.
|
|
9
6
|
*/
|
|
@@ -30,7 +27,6 @@ export enum FetchPolicy {
|
|
|
30
27
|
*/
|
|
31
28
|
NetworkOnly,
|
|
32
29
|
}
|
|
33
|
-
/* eslint-enable no-undef */
|
|
34
30
|
|
|
35
31
|
/**
|
|
36
32
|
* Define what can be cached.
|
|
@@ -88,7 +84,7 @@ export type ResponseCache = {
|
|
|
88
84
|
/**
|
|
89
85
|
* A cache with scoped sections.
|
|
90
86
|
*/
|
|
91
|
-
export type
|
|
87
|
+
export type RawScopedCache = {
|
|
92
88
|
/**
|
|
93
89
|
* The cache is scoped to allow easier clearing of different types of usage.
|
|
94
90
|
*/
|
|
@@ -116,3 +112,40 @@ export type ErrorOptions = {|
|
|
|
116
112
|
*/
|
|
117
113
|
cause?: ?Error,
|
|
118
114
|
|};
|
|
115
|
+
|
|
116
|
+
export interface ScopedCache {
|
|
117
|
+
set(scope: string, id: string, value: ValidCacheData): void;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Retrieve a value from the cache.
|
|
121
|
+
*/
|
|
122
|
+
get(scope: string, id: string): ?ValidCacheData;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Purge an item from the cache.
|
|
126
|
+
*/
|
|
127
|
+
purge(scope: string, id: string): void;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Purge a scope of items that match the given predicate.
|
|
131
|
+
*
|
|
132
|
+
* If the predicate is omitted, then all items in the scope are purged.
|
|
133
|
+
*/
|
|
134
|
+
purgeScope(
|
|
135
|
+
scope: string,
|
|
136
|
+
predicate?: (id: string, value: ValidCacheData) => boolean,
|
|
137
|
+
): void;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Purge all items from the cache that match the given predicate.
|
|
141
|
+
*
|
|
142
|
+
* If the predicate is omitted, then all items in the cache are purged.
|
|
143
|
+
*/
|
|
144
|
+
purgeAll(
|
|
145
|
+
predicate?: (
|
|
146
|
+
scope: string,
|
|
147
|
+
id: string,
|
|
148
|
+
value: ValidCacheData,
|
|
149
|
+
) => boolean,
|
|
150
|
+
): void;
|
|
151
|
+
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Exports / purgeSharedCache()"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# purgeSharedCache()
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
purgeSharedCache(scope?: string): void;
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
The `purgeSharedCache` method can be used to clear the shared in-memory cache used by the [`useSharedCache`](/docs/data-exports-usesharedcache--page) hook. Either a single scope or all scopes can be cleared.
|
|
19
|
-
|
|
20
|
-
Common uses for calling this method are during [server-side rendering](/docs/data-server-side-rendering-and-hydration--page) to ensure each render cycle remains isolated, or during testing in a `beforeEach` to cover for where previous test cases may have changed the shared cache.
|