@khanacademy/wonder-blocks-data 7.0.1 → 8.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 (53) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/es/index.js +284 -100
  3. package/dist/index.js +1180 -800
  4. package/package.json +1 -1
  5. package/src/__docs__/_overview_ssr_.stories.mdx +13 -13
  6. package/src/__docs__/exports.abort-inflight-requests.stories.mdx +20 -0
  7. package/src/__docs__/exports.data.stories.mdx +3 -3
  8. package/src/__docs__/{exports.fulfill-all-data-requests.stories.mdx → exports.fetch-tracked-requests.stories.mdx} +5 -5
  9. package/src/__docs__/exports.get-gql-request-id.stories.mdx +24 -0
  10. package/src/__docs__/{exports.has-unfulfilled-requests.stories.mdx → exports.has-tracked-requests-to-be-fetched.stories.mdx} +4 -4
  11. package/src/__docs__/exports.intialize-hydration-cache.stories.mdx +29 -0
  12. package/src/__docs__/exports.purge-caches.stories.mdx +23 -0
  13. package/src/__docs__/{exports.remove-all-from-cache.stories.mdx → exports.purge-hydration-cache.stories.mdx} +4 -4
  14. package/src/__docs__/{exports.clear-shared-cache.stories.mdx → exports.purge-shared-cache.stories.mdx} +4 -4
  15. package/src/__docs__/exports.track-data.stories.mdx +4 -4
  16. package/src/__docs__/exports.use-cached-effect.stories.mdx +7 -4
  17. package/src/__docs__/exports.use-gql.stories.mdx +1 -33
  18. package/src/__docs__/exports.use-server-effect.stories.mdx +1 -1
  19. package/src/__docs__/exports.use-shared-cache.stories.mdx +2 -2
  20. package/src/__docs__/types.fetch-policy.stories.mdx +44 -0
  21. package/src/__docs__/types.response-cache.stories.mdx +1 -1
  22. package/src/__tests__/generated-snapshot.test.js +5 -5
  23. package/src/components/__tests__/data.test.js +2 -6
  24. package/src/hooks/__tests__/use-cached-effect.test.js +341 -100
  25. package/src/hooks/__tests__/use-hydratable-effect.test.js +15 -9
  26. package/src/hooks/__tests__/use-shared-cache.test.js +6 -6
  27. package/src/hooks/use-cached-effect.js +169 -93
  28. package/src/hooks/use-hydratable-effect.js +8 -1
  29. package/src/hooks/use-shared-cache.js +2 -2
  30. package/src/index.js +14 -78
  31. package/src/util/__tests__/get-gql-request-id.test.js +74 -0
  32. package/src/util/__tests__/graphql-document-node-parser.test.js +542 -0
  33. package/src/util/__tests__/hydration-cache-api.test.js +35 -0
  34. package/src/util/__tests__/purge-caches.test.js +29 -0
  35. package/src/util/__tests__/request-api.test.js +188 -0
  36. package/src/util/__tests__/request-fulfillment.test.js +42 -0
  37. package/src/util/__tests__/ssr-cache.test.js +10 -60
  38. package/src/util/__tests__/to-gql-operation.test.js +42 -0
  39. package/src/util/data-error.js +6 -0
  40. package/src/util/get-gql-request-id.js +50 -0
  41. package/src/util/graphql-document-node-parser.js +133 -0
  42. package/src/util/graphql-types.js +30 -0
  43. package/src/util/hydration-cache-api.js +28 -0
  44. package/src/util/purge-caches.js +15 -0
  45. package/src/util/request-api.js +66 -0
  46. package/src/util/request-fulfillment.js +32 -12
  47. package/src/util/request-tracking.js +1 -1
  48. package/src/util/ssr-cache.js +1 -21
  49. package/src/util/to-gql-operation.js +44 -0
  50. package/src/util/types.js +31 -0
  51. package/src/__docs__/exports.intialize-cache.stories.mdx +0 -29
  52. package/src/__docs__/exports.remove-from-cache.stories.mdx +0 -25
  53. package/src/__docs__/exports.request-fulfillment.stories.mdx +0 -36
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-data",
3
- "version": "7.0.1",
3
+ "version": "8.0.0",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -56,11 +56,11 @@ renderToString(trackedElement);
56
56
 
57
57
  After each render, we want to see if there are any tracked requests needing to be fulfilled. If there are, we will want to fulfill them and render again; if there are not, we are ready to finish the page rendering and move on.
58
58
 
59
- To determine which path to take, we can use [`hasUnfullfilledRequests`](/docs/data-exports-hasunfulfilledrequests--page), and then based off that result, either fulfill the requests, or finish rendering our page.
59
+ To determine which path to take, we can use [`hasTrackedRequestsToBeFetched`](/docs/data-exports-hastrackedrequeststobefetched--page), and then based off that result, either fulfill the requests, or finish rendering our page.
60
60
 
61
61
  ```ts
62
- if (hasUnfulfilledRequests()) {
63
- await fulfillAllDataRequests();
62
+ if (hasTrackedRequestsToBeFetched()) {
63
+ await fetchTrackedRequests();
64
64
 
65
65
  // Render again.
66
66
  // ...
@@ -72,7 +72,7 @@ if (hasUnfulfilledRequests()) {
72
72
 
73
73
  Internally, Wonder Blocks Data caches the responses of fulfilled requests and does not track them again. Each cycle of the rendering should occur with the same knowledge you expect the client-side to have when it performs the render, so although we want to retain our hydration cache response, we need to make sure any transient caches are cleared.
74
74
 
75
- Once the rendered component no longer requires any further data, the cached responses can be obtained to include in the rendered page for the client to hydrate. The hydration cache is promised by `fulfillAllDataRequests()`, regardless of whether it actually had any pending requests or not.
75
+ Once the rendered component no longer requires any further data, the cached responses can be obtained to include in the rendered page for the client to hydrate. The hydration cache is promised by `fetchTrackedRequests()`, regardless of whether it actually had any pending requests or not.
76
76
 
77
77
  ### Putting it all together
78
78
 
@@ -84,9 +84,9 @@ A function for server-side rendering with Wonder Blocks Data could look somethin
84
84
  import {Server} from "@khanacademy/wonder-blocks-core";
85
85
  import {
86
86
  TrackData,
87
- hasUnfulfilledRequests,
88
- fulfillAllDataRequests,
89
- clearSharedCache,
87
+ hasTrackedRequestsToBeFetched,
88
+ fetchTrackedRequests,
89
+ purgeSharedCache,
90
90
  } from "@khanacademy/wonder-blocks-data";
91
91
 
92
92
  // Don't forget to import your app!
@@ -113,14 +113,14 @@ async function renderApp(): Promise<string> {
113
113
  * shared cache used by the `useSharedCache` hook as this is transient
114
114
  * cache that does not itself get directly hydrated.
115
115
  */
116
- clearSharedCache();
116
+ purgeSharedCache();
117
117
 
118
118
  // Render the tracked component.
119
119
  renderedComponent = renderToString(trackedElement);
120
120
 
121
- if (hasUnfulfilledRequests()) {
121
+ if (hasTrackedRequestsToBeFetched()) {
122
122
  // Fulfill any pending requests.
123
- await fulfillAllDataRequests();
123
+ await fetchTrackedRequests();
124
124
 
125
125
  // We can't leave the loop yet as we want to render the element
126
126
  // again with the newly fulfilled data.
@@ -130,7 +130,7 @@ async function renderApp(): Promise<string> {
130
130
 
131
131
  // We rendered with all the data fulfilled, so we can grab the
132
132
  // hydration cache contents and exit the loop now.
133
- hydrationCache = await fulfillAllDataRequests();
133
+ hydrationCache = await fetchTrackedRequests();
134
134
  } while (hydrationCache == null);
135
135
 
136
136
  // Finally, render the page with our hydration cache and rendered
@@ -164,12 +164,12 @@ In the previous section, this was displayed as the `hydrate.js` script. Here is
164
164
 
165
165
  ```tsx
166
166
  import {hydrate} from "react-dom";
167
- import {initializeCache} from "@khanacademy/wonder-blocks-data";
167
+ import {initializeHydrationCache} from "@khanacademy/wonder-blocks-data";
168
168
 
169
169
  // Don't forget to import your app!
170
170
  import App from "./App.js";
171
171
 
172
- initializeCache(window._WONDER_BLOCKS_DATA_);
172
+ initializeHydrationCache(window._WONDER_BLOCKS_DATA_);
173
173
 
174
174
  React.hydrate(
175
175
  // This should match whatever was passed to the server-side rendering
@@ -0,0 +1,20 @@
1
+ import {Meta} from "@storybook/addon-docs";
2
+
3
+ <Meta
4
+ title="Data / Exports / abortInflightRequests()"
5
+ parameters={{
6
+ chromatic: {
7
+ disableSnapshot: true,
8
+ },
9
+ }}
10
+ />
11
+
12
+ # abortInflightRequests()
13
+
14
+ ```ts
15
+ abortInflightRequests(): void;
16
+ ```
17
+
18
+ Aborts all inflight requests that were started via [`useCachedEffect`](/docs/data-exports-usecachedeffect--page), [`useHydratableEffect`](/docs/data-exports-usehydratableeffect--page), or [`fetchTrackedRequests()`](/docs/data-exports-fetchtrackedrequests--page).
19
+
20
+ NOTE: Full abort signalling is not currently implemented. The effect of this call is to remove any inflight requests from our inflight request tracking such that a new matching request will cause a new fetch to occur rather than sharing any existing one.
@@ -105,12 +105,12 @@ const myInvalidHandler = () => new Promise((resolve, reject) =>
105
105
 
106
106
  If the hydration cache already contains data or an error for our request, then
107
107
  the `Data` component will render it immediately. The hydration cache is
108
- populated using the `initializeCache` method before rendering.
108
+ populated using the `initializeHydrationCache` method before rendering.
109
109
 
110
110
  ```jsx
111
111
  import {Body, BodyMonospace} from "@khanacademy/wonder-blocks-typography";
112
112
  import {View} from "@khanacademy/wonder-blocks-core";
113
- import {Data, initializeCache} from "@khanacademy/wonder-blocks-data";
113
+ import {Data, initializeHydrationCache} from "@khanacademy/wonder-blocks-data";
114
114
  import {Strut} from "@khanacademy/wonder-blocks-layout";
115
115
  import Color from "@khanacademy/wonder-blocks-color";
116
116
  import Spacing from "@khanacademy/wonder-blocks-spacing";
@@ -121,7 +121,7 @@ const myHandler = () => {
121
121
  );
122
122
  };
123
123
 
124
- initializeCache({
124
+ initializeHydrationCache({
125
125
  DATA: {
126
126
  data: "I'm DATA from the hydration cache"
127
127
  },
@@ -1,7 +1,7 @@
1
1
  import {Meta} from "@storybook/addon-docs";
2
2
 
3
3
  <Meta
4
- title="Data / Exports / fulfillAllDataRequests()"
4
+ title="Data / Exports / fetchTrackedRequests()"
5
5
  parameters={{
6
6
  chromatic: {
7
7
  disableSnapshot: true,
@@ -9,16 +9,16 @@ import {Meta} from "@storybook/addon-docs";
9
9
  }}
10
10
  />
11
11
 
12
- # fulfillAllDataRequests()
12
+ # fetchTrackedRequests()
13
13
 
14
14
  ```ts
15
- fulfillAllDataRequests(): Promise<ResponseCache>;
15
+ fetchTrackedRequests(): Promise<ResponseCache>;
16
16
  ```
17
17
 
18
- When performing server-side rendering (SSR), the data requests that are being made via the [`Data`](/docs/data-exports-data--page) component can be tracked by rendering the React tree inside the [`TrackData`](/docs/data-exports-trackdata--page) component. After this has occurred, the tracked requests can be fulfilled using `fulfillAllDataRequests`.
18
+ When performing server-side rendering (SSR), the data requests that are being made via the [`Data`](/docs/data-exports-data--page) component can be tracked by rendering the React tree inside the [`TrackData`](/docs/data-exports-trackdata--page) component. After this has occurred, the tracked requests can be fulfilled using `fetchTrackedRequests`.
19
19
 
20
20
  This method returns a promise that resolves to a copy of the data that was cached by fulfilling the tracked requests. In the process, it clears the record of tracked requests so that new requests can be tracked and fulfilled if so required.
21
21
 
22
- The returned copy of the data cache can be used with the [`initializeCache`](/docs/data-exports-initializecache--page) method to prepare the data cache before a subsequent render. This is useful on the server to then SSR a more complete result, and again on the client, to rehydrate that result.
22
+ The returned copy of the data cache can be used with the [`initializeHydrationCache`](/docs/data-exports-initializehydrationcache--page) method to prepare the data cache before a subsequent render. This is useful on the server to then SSR a more complete result, and again on the client, to rehydrate that result.
23
23
 
24
24
  More details about server-side rendering with Wonder Blocks Data can be found in the [relevant overview section](/docs/data-server-side-rendering-and-hydration--page).
@@ -0,0 +1,24 @@
1
+ import {Meta} from "@storybook/addon-docs";
2
+
3
+ <Meta
4
+ title="Data / Exports / getGqlRequestId()"
5
+ parameters={{
6
+ chromatic: {
7
+ disableSnapshot: true,
8
+ },
9
+ }}
10
+ />
11
+
12
+ # getGqlRequestId()
13
+
14
+ ```ts
15
+ function getGqlRequestId<TData, TVariables: {...}>(
16
+ operation: GqlOperation<TData, TVariables>,
17
+ variables: ?TVariables,
18
+ context: GqlContext,
19
+ ): string;
20
+ ```
21
+
22
+ The `getGqlRequestId` function generates an identifier based on the operation, variables, and context of a specific GraphQL request. This identifier is guaranteed to be the same for all requests that share the same operation, variables, and context, even if variables and context values are in different orders.
23
+
24
+ The identifier returned by this function can then be used with our [`useCachedEffect`](/docs/data-exports-usecachedeffect--page), [`useServerEffect`](/docs/data-exports-useservereffect--page), and [`useHydratableEffect`](/docs/data-exports-usehydratableeffect--page) hooks as the `requestId` parameter, allowing them to be combined with the fetch operation from the [`useGql`](/docs/data-exports-usegql--page) hook.
@@ -1,7 +1,7 @@
1
1
  import {Meta} from "@storybook/addon-docs";
2
2
 
3
3
  <Meta
4
- title="Data / Exports / hasUnfulfilledRequests()"
4
+ title="Data / Exports / hasTrackedRequestsToBeFetched()"
5
5
  parameters={{
6
6
  chromatic: {
7
7
  disableSnapshot: true,
@@ -9,12 +9,12 @@ import {Meta} from "@storybook/addon-docs";
9
9
  }}
10
10
  />
11
11
 
12
- # hasUnfulfilledRequests()
12
+ # hasTrackedRequestsToBeFetched()
13
13
 
14
14
  ```ts
15
- hasUnfulfilledRequests(): boolean;
15
+ hasTrackedRequestsToBeFetched(): boolean;
16
16
  ```
17
17
 
18
- When performing server-side rendering (SSR), any requests that have been tracked will cause this method to return `true`. Once [`fulfillAllDataRequests`](/docs/data-exports-fulfillalldatarequests--page) has been called and the promise is settled, this method will return `false` to indicate that there are no more pending requests.
18
+ When performing server-side rendering (SSR), any requests that have been tracked will cause this method to return `true`. Once [`fetchTrackedRequests`](/docs/data-exports-fetchtrackedrequests--page) has been called and the promise is settled, this method will return `false` to indicate that there are no more pending requests.
19
19
 
20
20
  More details about server-side rendering with Wonder Blocks Data can be found in the [relevant overview section](/docs/data-server-side-rendering-and-hydration--page).
@@ -0,0 +1,29 @@
1
+ import {Meta} from "@storybook/addon-docs";
2
+
3
+ <Meta
4
+ title="Data / Exports / initializeHydrationCache()"
5
+ parameters={{
6
+ chromatic: {
7
+ disableSnapshot: true,
8
+ },
9
+ }}
10
+ />
11
+
12
+ # initializeHydrationCache()
13
+
14
+ ```ts
15
+ initializeHydrationCache(sourceCache: ResponseCache): void;
16
+ ```
17
+
18
+ | Argument | Flow&nbsp;Type | Default | Description |
19
+ | --- | --- | --- | --- |
20
+ | `sourceData` | `ResponseCache` | _Required_ | The source cache that will be used to initialize the response cache. |
21
+
22
+ Wonder Blocks Data caches data in its response cache for hydration. This cache can be initialized with data using the `initializeHydrationCache` method.
23
+ The `initializeHydrationCache` method can only be called when the hydration cache is empty.
24
+
25
+ Usually, the data to be passed to `v` will be obtained by calling [`fetchTrackedRequests`](/docs/data-exports-fetchtrackedrequests--page) after tracking data requests (see [`TrackData`](/docs/data-exports-trackdata--page)) during server-side rendering.
26
+
27
+ Combine with [`purgeHydrationCache`](/docs/data-exports-purgehydrationcache--page) to support your testing needs.
28
+
29
+ More details about server-side rendering with Wonder Blocks Data can be found in the [relevant overview section](/docs/data-server-side-rendering-and-hydration--page).
@@ -0,0 +1,23 @@
1
+ import {Meta} from "@storybook/addon-docs";
2
+
3
+ <Meta
4
+ title="Data / Exports / purgeCaches()"
5
+ parameters={{
6
+ chromatic: {
7
+ disableSnapshot: true,
8
+ },
9
+ }}
10
+ />
11
+
12
+ # purgeCaches()
13
+
14
+ ```ts
15
+ purgeCaches(scope?: string): void;
16
+ ```
17
+
18
+ The `purgeCaches` method will purge the following caches managed by Wonder Blocks Data:
19
+
20
+ - Shared in-memory cache as used by [`useSharedCache`](/docs/data-exports-usesharedcache--page) and other hooks
21
+ - Hydration cache as used during server-side rendering
22
+
23
+ This is equivalent to calling both `purgeSharedCache()` and `purgeHydrationCache()`, and is especially useful when writing tests or setting up a test environment.
@@ -1,7 +1,7 @@
1
1
  import {Meta} from "@storybook/addon-docs";
2
2
 
3
3
  <Meta
4
- title="Data / Exports / removeAllFromCache()"
4
+ title="Data / Exports / purgeHydrationCache()"
5
5
  parameters={{
6
6
  chromatic: {
7
7
  disableSnapshot: true,
@@ -9,10 +9,10 @@ import {Meta} from "@storybook/addon-docs";
9
9
  }}
10
10
  />
11
11
 
12
- # removeAllFromCache()
12
+ # purgeHydrationCache()
13
13
 
14
14
  ```ts
15
- removeAllFromCache(predicate?: (key: string, cacheEntry: $ReadOnly<CachedResponse<ValidCacheData>>) => boolean): void;
15
+ purgeHydrationCache(predicate?: (key: string, cacheEntry: $ReadOnly<CachedResponse<ValidCacheData>>) => boolean): void;
16
16
  ```
17
17
 
18
18
  | Argument | Flow&nbsp;Type | Default | Description |
@@ -21,4 +21,4 @@ removeAllFromCache(predicate?: (key: string, cacheEntry: $ReadOnly<CachedRespons
21
21
 
22
22
  Removes all entries that match a given predicate from the cache. If no predicate is given, all cached entries.
23
23
 
24
- This can be used after [`initializeCache`](/docs/data-exports-initializecache--page) to manipulate the cache prior to hydration wich can be useful during testing (especially to clear the cache so that it can be initialized again).
24
+ This can be used after [`initializeHydrationCache`](/docs/data-exports-initializehydrationcache--page) to manipulate the cache prior to hydration wich can be useful during testing (especially to clear the cache so that it can be initialized again).
@@ -1,7 +1,7 @@
1
1
  import {Meta} from "@storybook/addon-docs";
2
2
 
3
3
  <Meta
4
- title="Data / Exports / clearSharedCache()"
4
+ title="Data / Exports / purgeSharedCache()"
5
5
  parameters={{
6
6
  chromatic: {
7
7
  disableSnapshot: true,
@@ -9,12 +9,12 @@ import {Meta} from "@storybook/addon-docs";
9
9
  }}
10
10
  />
11
11
 
12
- # clearSharedCache()
12
+ # purgeSharedCache()
13
13
 
14
14
  ```ts
15
- clearSharedCache(scope?: string): void;
15
+ purgeSharedCache(scope?: string): void;
16
16
  ```
17
17
 
18
- The `clearSharedCache` method can be used to clear the shared in-memory cache used by the [`useSharedCache`](/docs/data-exports-clearsharedcache--page) hook. Either a single scope or all scopes can be cleared.
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
19
 
20
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.
@@ -71,7 +71,7 @@ class ErrorBoundary extends React.Component {
71
71
 
72
72
  When used server-side, this component tracks any data requests made through
73
73
  the `Data` component during a render cycle. This data can then be obtained
74
- using the `fulfillAllDataRequests` method. The data can then be used in an
74
+ using the `fetchTrackedRequests` method. The data can then be used in an
75
75
  additional render cycle to render with that data.
76
76
 
77
77
  ```jsx
@@ -80,7 +80,7 @@ import {Strut} from "@khanacademy/wonder-blocks-layout";
80
80
  import Spacing from "@khanacademy/wonder-blocks-spacing";
81
81
  import Button from "@khanacademy/wonder-blocks-button";
82
82
  import {Server, View} from "@khanacademy/wonder-blocks-core";
83
- import {Data, TrackData, fulfillAllDataRequests} from "@khanacademy/wonder-blocks-data";
83
+ import {Data, TrackData, fetchTrackedRequests} from "@khanacademy/wonder-blocks-data";
84
84
 
85
85
  const myPretendHandler = () => new Promise((resolve, reject) =>
86
86
  setTimeout(() => resolve("DATA!"), 3000),
@@ -158,7 +158,7 @@ class Example extends React.Component {
158
158
  <Body>
159
159
  The above components requested data, but we're server-side,
160
160
  so all that happened is we tracked the request.
161
- In this example, we've also called `fulfillAllDataRequests`
161
+ In this example, we've also called `fetchTrackedRequests`
162
162
  to fetch that tracked data.
163
163
  </Body>
164
164
  <Strut size={Spacing.small_12} />
@@ -195,7 +195,7 @@ class Example extends React.Component {
195
195
  );
196
196
  } finally {
197
197
  if (!this.state.data && Server.isServerSide()) {
198
- setTimeout(() => fulfillAllDataRequests().then((data) => {
198
+ setTimeout(() => fetchTrackedRequests().then((data) => {
199
199
  if (this._mounted) {
200
200
  this.setState({data});
201
201
  }
@@ -16,13 +16,14 @@ function useCachedEffect<TData: ValidCacheData>(
16
16
  requestId: string,
17
17
  handler: () => Promise<TData>,
18
18
  options?: CachedEffectOptions<TData>,
19
- ): Result<TData>;
19
+ ): [Result<TData>, () => void];
20
20
  ```
21
21
 
22
- This hook is a wrapper around `useEffect`. Internally it uses the [`useSharedCache`](/docs/data-exports-usesharedcache--page) hook to store the result of the effect and, if that result is available, returns it without rerunning the effect. The precise behavior of the
23
- hook can be modified using the options.
22
+ This hook invokes the given handler and caches the result using the [`useSharedCache`](/docs/data-exports-usesharedcache--page) hook. The `requestId` is used to both identify inflight requests that can be shared, and to identify the cached value to use.
24
23
 
25
- There is currently no way to reinvoke the effect without changing the `requestId`.
24
+ The hook returns an array containing the current state of the request, and a function that can be used to `refetch` that request on demand. Calling `refetch` while an inflight request is in progress for the given `requestId` will be a no-op.
25
+
26
+ The behavior of the hook can be modified with the options.
26
27
 
27
28
  ```ts
28
29
  type CachedEffectOptions<TData: ValidCacheData> = {|
@@ -30,6 +31,7 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
30
31
  retainResultOnChange?: boolean,
31
32
  onResultChanged?: (result: Result<TData>) => void,
32
33
  scope?: string,
34
+ fetchPolicy?: FetchPolicy,
33
35
  |};
34
36
  ```
35
37
 
@@ -39,3 +41,4 @@ type CachedEffectOptions<TData: ValidCacheData> = {|
39
41
  | `retainResultOnChange` | `false` | When `true`, the effect will not reset the result to the loading status while executing if the requestId changes, instead, returning the existing result from before the change; otherwise, the result will be set to loading status. If the status is loading when the changes are made, it will remain as loading; old pending effects are discarded on changes and as such this value has no effect in that case.|
40
42
  | `onResultChanged` | `undefined` | Callback that is invoked if the result for the given hook has changed. When defined, the hook will invoke this callback whenever it has reason to change the result and will not otherwise affect component rendering directly. When not defined, the hook will ensure the component re-renders to pick up the latest result. |
41
43
  | `scope` | `"useCachedEffect"` | Scope to use with the shared cache. When specified, the given scope will be used to isolate this hook's cached results. Otherwise, the default scope will be used. Changing this value after the first call is not supported. |
44
+ | `fetchPolicy` | [`FetchPolicy`](/docs/data-types-fetchpolicy--page) | Fetch policy to use when fetching the data. Defaults to `FetchPolicy.CacheBeforeNetwork`. |
@@ -30,39 +30,7 @@ The return value of `useGql` is a fetch function that can be used to invoke a Gr
30
30
 
31
31
  The result of calling the function returned by `useGql` is a promise of the data that the request will return. This is compatible with the [`useServerEffect`](/docs/data-exports-useservereffect--page), [`useCachedEffect`](/docs/data-exports-usecachedeffect--page), and [`useHydratableEffect`](/docs/data-exports-usehydratableeffect--page) hooks, allowing a variety of scenarios to be easily constructed.
32
32
 
33
- Below is an example request identifier generation function that could be used to generate request identifiers (we hope to include a base implementation for this purpose in a future release of Wonder Blocks Data):
34
-
35
- ```ts
36
- const getGqlRequestId = <TData, TVariables: {...}>(
37
- operation: GqlOperation<TData, TVariables>,
38
- variables: ?TVariables,
39
- context: GqlContext,
40
- ): string => {
41
- // We add all the bits for this into an array and then join them with
42
- // a chosen separator.
43
- const parts = [];
44
- parts.push(operation.id);
45
- if (context != null) {
46
- // Turn the context into a string of the form,
47
- // "key1=;key2=value"
48
- const sortedContext = Object.keys(context || {})
49
- .sort()
50
- .map((key) => `${key}=${JSON.stringify(context?.[key]) || ""}`)
51
- .join(";");
52
- parts.push(sortedContext);
53
- }
54
- if (variables != null) {
55
- // Turn the variables into a string of the form,
56
- // "key1=;key2=value"
57
- const sortedVariables = Object.keys(variables || {})
58
- .sort()
59
- .map((key) => `${key}=${JSON.stringify(variables?.[key]) || ""}`)
60
- .join(";");
61
- parts.push(sortedVariables);
62
- }
63
- return parts.join("|");
64
- };
65
- ```
33
+ Use [`getGqlRequestId`](/docs/data-exports-getgqlrequestid--page) to get a request ID that can be used with these hooks.
66
34
 
67
35
  ## Context Merging
68
36
 
@@ -39,7 +39,7 @@ First, this hook checks the server-side rendering cache for the request identifi
39
39
 
40
40
  If there is no cached value, it will return a "loading" state. In addition, if the current rendering component has a [`TrackData`](/docs/data-exports-trackdata--page) ancestor, `useServerEffect` will register the request for fulfillment.
41
41
 
42
- This then allows that pending request to be fulfilled with [`fulfillAllDataRequests`](/docs/data-exports-fulfillalldatarequests--page), the response to be placed into the cache, and the render to be reexecuted, at which point, this hook will be able to provide that result instead of "loading.
42
+ This then allows that pending request to be fulfilled with [`fetchTrackedRequests`](/docs/data-exports-fetchtrackddatarequests--page), the response to be placed into the cache, and the render to be reexecuted, at which point, this hook will be able to provide that result instead of "loading.
43
43
 
44
44
  More details about server-side rendering with Wonder Blocks Data can be found in the [relevant overview section](/docs/data-server-side-rendering-and-hydration--page).
45
45
 
@@ -19,12 +19,12 @@ function useSharedCache<TValue: ValidCacheData>(
19
19
  ): [?TValue, CacheValueFn<TValue>];
20
20
  ```
21
21
 
22
- The `useSharedCache` hook provides access to a shared in-memory cache. This cache is not part of the cache hydrated by Wonder Blocks Data, so [`clearSharedCache`](/docs/data-exports-clearsharedcache--page) must be called between server-side render cycles.
22
+ The `useSharedCache` hook provides access to a shared in-memory cache. This cache is not part of the cache hydrated by Wonder Blocks Data, so [`purgeSharedCache`](/docs/data-exports-purgesharedcache--page) must be called between server-side render cycles.
23
23
 
24
24
  The hook returns a tuple of the currently cached value, or `null` if none is cached, and a function that can be used to set the cached value.
25
25
 
26
26
  The shared cache is passive and as such does not notify of changes to its contents.
27
27
 
28
- Each cached item is identified by an id and a scope. The scope is used to group items. Whole scopes can be cleared by specifying the specific scope when calling [`clearSharedCache`](/docs/data-exports-clearsharedcache--page).
28
+ Each cached item is identified by an id and a scope. The scope is used to group items. Whole scopes can be cleared by specifying the specific scope when calling [`purgeSharedCache`](/docs/data-exports-purgesharedcache--page).
29
29
 
30
30
  An optional argument, `initialValue` can be given. This can be either the value to be cached itself or a function that returns the value to be cached (functions themselves are not valid cachable values). This allows for expensive initialization to only occur when it is necessary.
@@ -0,0 +1,44 @@
1
+ import {Meta} from "@storybook/addon-docs";
2
+
3
+ <Meta
4
+ title="Data / Types / FetchPolicy"
5
+ parameters={{
6
+ chromatic: {
7
+ disableSnapshot: true,
8
+ },
9
+ }}
10
+ />
11
+
12
+ # FetchPolicy
13
+
14
+ ```ts
15
+ export enum FetchPolicy {
16
+ /**
17
+ * If the data is in the cache, return that; otherwise, fetch from the server.
18
+ */
19
+ CacheBeforeNetwork,
20
+
21
+ /**
22
+ * If the data is in the cache, return that; always fetch from the server
23
+ * regardless of cache.
24
+ */
25
+ CacheAndNetwork,
26
+
27
+ /**
28
+ * If the data is in the cache, return that; otherwise, do nothing.
29
+ */
30
+ CacheOnly,
31
+
32
+ /**
33
+ * Ignore any existing cached result; fetch from the server.
34
+ */
35
+ NetworkOnly,
36
+ }
37
+ ```
38
+
39
+ The `FetchPolicy` type is used with our request framework to define how a request should be fulfilled with respect to the cache and the network.
40
+
41
+ * `CacheBeforeNetwork`: If the data is in the cache, return that; otherwise, fetch from the server.
42
+ * `CacheAndNetwork`: If the data is in the cache, return that; always fetch from the server regardless of cache.
43
+ * `CacheOnly`: If the data is in the cache, return that; otherwise, do nothing.
44
+ * `NetworkOnly`: Ignore any existing cached result; always fetch from the server.
@@ -18,7 +18,7 @@ type ResponseCache = {
18
18
  };
19
19
  ```
20
20
 
21
- `ResponseCache` describes the serialized cache that is used to hydrate responses. An example of a valid `ResponseCache` instance is shown below. Generally, you would not generate this object directly, but rather use the returned data from [`fulfillAllDataRequests`](/docs/data-exports-fulfillalldatarequests--page).
21
+ `ResponseCache` describes the serialized cache that is used to hydrate responses. An example of a valid `ResponseCache` instance is shown below. Generally, you would not generate this object directly, but rather use the returned data from [`fetchTrackedRequests`](/docs/data-exports-fetchtrackedrequests--page).
22
22
 
23
23
  ```ts
24
24
  const responseCache: ResponseCache = {
@@ -12,10 +12,10 @@ import {Body, BodyMonospace} from "@khanacademy/wonder-blocks-typography";
12
12
  import {View, Server} from "@khanacademy/wonder-blocks-core";
13
13
  import {
14
14
  Data,
15
- initializeCache,
15
+ initializeHydrationCache,
16
16
  InterceptRequests,
17
17
  TrackData,
18
- fulfillAllDataRequests,
18
+ fetchTrackedRequests,
19
19
  } from "@khanacademy/wonder-blocks-data";
20
20
  import {Strut} from "@khanacademy/wonder-blocks-layout";
21
21
  import Color from "@khanacademy/wonder-blocks-color";
@@ -82,7 +82,7 @@ describe("wonder-blocks-data", () => {
82
82
  );
83
83
  };
84
84
 
85
- initializeCache({
85
+ initializeHydrationCache({
86
86
  DATA: {
87
87
  data: "I'm DATA from the hydration cache",
88
88
  },
@@ -277,7 +277,7 @@ describe("wonder-blocks-data", () => {
277
277
  The above components requested data, but we're
278
278
  server-side, so all that happened is we tracked
279
279
  the request. In this example, we've also called
280
- `fulfillAllDataRequests` to fetch that tracked
280
+ `fetchTrackedRequests` to fetch that tracked
281
281
  data.
282
282
  </Body>
283
283
  <Strut size={Spacing.small_12} />
@@ -329,7 +329,7 @@ describe("wonder-blocks-data", () => {
329
329
  if (!this.state.data && Server.isServerSide()) {
330
330
  setTimeout(
331
331
  () =>
332
- fulfillAllDataRequests().then((data) => {
332
+ fetchTrackedRequests().then((data) => {
333
333
  if (this._mounted) {
334
334
  this.setState({
335
335
  data,
@@ -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 {clearSharedCache} from "../../hooks/use-shared-cache.js";
10
+ import {purgeSharedCache} 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
- clearSharedCache();
27
+ purgeSharedCache();
28
28
 
29
29
  const responseCache = new SsrCache();
30
30
  jest.spyOn(SsrCache, "Default", "get").mockReturnValue(responseCache);
@@ -36,10 +36,6 @@ describe("Data", () => {
36
36
  );
37
37
  });
38
38
 
39
- afterEach(() => {
40
- jest.resetAllMocks();
41
- });
42
-
43
39
  describe("CSR: isServerSide false", () => {
44
40
  beforeEach(() => {
45
41
  jest.spyOn(Server, "isServerSide").mockReturnValue(false);