@khanacademy/wonder-blocks-data 10.0.5 → 10.1.1
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 +38 -0
- package/dist/components/data.d.ts +52 -0
- package/dist/components/data.js.flow +63 -0
- package/dist/components/gql-router.d.ts +24 -0
- package/dist/components/gql-router.js.flow +33 -0
- package/dist/components/intercept-context.d.ts +10 -0
- package/dist/components/intercept-context.js.flow +19 -0
- package/dist/components/intercept-requests.d.ts +42 -0
- package/dist/components/intercept-requests.js.flow +51 -0
- package/dist/components/track-data.d.ts +11 -0
- package/dist/components/track-data.js.flow +18 -0
- package/dist/es/index.js +196 -214
- package/dist/hooks/use-cached-effect.d.ts +70 -0
- package/dist/hooks/use-cached-effect.js.flow +85 -0
- package/dist/hooks/use-gql-router-context.d.ts +5 -0
- package/dist/hooks/use-gql-router-context.js.flow +15 -0
- package/dist/hooks/use-gql.d.ts +12 -0
- package/dist/hooks/use-gql.js.flow +29 -0
- package/dist/hooks/use-hydratable-effect.d.ts +102 -0
- package/dist/hooks/use-hydratable-effect.js.flow +125 -0
- package/dist/hooks/use-request-interception.d.ts +14 -0
- package/dist/hooks/use-request-interception.js.flow +25 -0
- package/dist/hooks/use-server-effect.d.ts +39 -0
- package/dist/hooks/use-server-effect.js.flow +51 -0
- package/dist/hooks/use-shared-cache.d.ts +32 -0
- package/dist/hooks/use-shared-cache.js.flow +43 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +198 -219
- package/dist/index.js.flow +48 -2
- package/dist/util/data-error.d.ts +45 -0
- package/dist/util/data-error.js.flow +64 -0
- package/dist/util/get-gql-data-from-response.d.ts +4 -0
- package/dist/util/get-gql-data-from-response.js.flow +13 -0
- package/dist/util/get-gql-request-id.d.ts +5 -0
- package/dist/util/get-gql-request-id.js.flow +20 -0
- package/dist/util/gql-error.d.ts +28 -0
- package/dist/util/gql-error.js.flow +43 -0
- package/dist/util/gql-router-context.d.ts +3 -0
- package/dist/util/gql-router-context.js.flow +10 -0
- package/dist/util/gql-types.d.ts +34 -0
- package/dist/util/gql-types.js.flow +53 -0
- package/dist/util/graphql-document-node-parser.d.ts +18 -0
- package/dist/util/graphql-document-node-parser.js.flow +31 -0
- package/dist/util/graphql-types.d.ts +19 -0
- package/dist/util/graphql-types.js.flow +30 -0
- package/dist/util/hydration-cache-api.d.ts +17 -0
- package/dist/util/hydration-cache-api.js.flow +30 -0
- package/dist/util/merge-gql-context.d.ts +8 -0
- package/dist/util/merge-gql-context.js.flow +19 -0
- package/dist/util/purge-caches.d.ts +8 -0
- package/dist/util/purge-caches.js.flow +15 -0
- package/dist/util/request-api.d.ts +28 -0
- package/dist/util/request-api.js.flow +34 -0
- package/dist/util/request-fulfillment.d.ts +37 -0
- package/dist/util/request-fulfillment.js.flow +50 -0
- package/dist/util/request-tracking.d.ts +62 -0
- package/dist/util/request-tracking.js.flow +81 -0
- package/dist/util/result-from-cache-response.d.ts +5 -0
- package/dist/util/result-from-cache-response.js.flow +15 -0
- package/dist/util/scoped-in-memory-cache.d.ts +38 -0
- package/dist/util/scoped-in-memory-cache.js.flow +57 -0
- package/dist/util/serializable-in-memory-cache.d.ts +16 -0
- package/dist/util/serializable-in-memory-cache.js.flow +26 -0
- package/dist/util/ssr-cache.d.ts +51 -0
- package/dist/util/ssr-cache.js.flow +87 -0
- package/dist/util/status.d.ts +10 -0
- package/dist/util/status.js.flow +19 -0
- package/dist/util/to-gql-operation.d.ts +32 -0
- package/dist/util/to-gql-operation.js.flow +45 -0
- package/dist/util/types.d.ts +111 -0
- package/dist/util/types.js.flow +151 -0
- package/package.json +5 -6
- package/src/components/__tests__/{data.test.js → data.test.tsx} +50 -16
- package/src/components/__tests__/{gql-router.test.js → gql-router.test.tsx} +6 -7
- package/src/components/__tests__/{intercept-requests.test.js → intercept-requests.test.tsx} +4 -5
- package/src/components/__tests__/{track-data.test.js → track-data.test.tsx} +4 -5
- package/src/components/{data.js → data.ts} +13 -21
- package/src/components/{gql-router.js → gql-router.tsx} +14 -16
- package/src/components/{intercept-context.js → intercept-context.ts} +5 -4
- package/src/components/{intercept-requests.js → intercept-requests.tsx} +9 -10
- package/src/components/{track-data.js → track-data.tsx} +5 -6
- package/src/hooks/__tests__/{use-cached-effect.test.js → use-cached-effect.test.tsx} +65 -63
- package/src/hooks/__tests__/{use-gql-router-context.test.js → use-gql-router-context.test.tsx} +9 -9
- package/src/hooks/__tests__/{use-gql.test.js → use-gql.test.tsx} +23 -24
- package/src/hooks/__tests__/{use-hydratable-effect.test.js → use-hydratable-effect.test.ts} +52 -54
- package/src/hooks/__tests__/{use-request-interception.test.js → use-request-interception.test.tsx} +7 -5
- package/src/hooks/__tests__/{use-server-effect.test.js → use-server-effect.test.ts} +16 -10
- package/src/hooks/__tests__/{use-shared-cache.test.js → use-shared-cache.test.ts} +13 -13
- package/src/hooks/{use-cached-effect.js → use-cached-effect.ts} +34 -31
- package/src/hooks/{use-gql-router-context.js → use-gql-router-context.ts} +6 -7
- package/src/hooks/{use-gql.js → use-gql.ts} +9 -9
- package/src/hooks/{use-hydratable-effect.js → use-hydratable-effect.ts} +60 -67
- package/src/hooks/{use-request-interception.js → use-request-interception.ts} +6 -6
- package/src/hooks/{use-server-effect.js → use-server-effect.ts} +12 -14
- package/src/hooks/{use-shared-cache.js → use-shared-cache.ts} +16 -11
- package/src/index.ts +46 -0
- package/src/util/__tests__/{get-gql-data-from-response.test.js → get-gql-data-from-response.test.ts} +1 -2
- package/src/util/__tests__/{get-gql-request-id.test.js → get-gql-request-id.test.ts} +10 -12
- package/src/util/__tests__/{graphql-document-node-parser.test.js → graphql-document-node-parser.test.ts} +12 -13
- package/src/util/__tests__/{hydration-cache-api.test.js → hydration-cache-api.test.ts} +3 -4
- package/src/util/__tests__/{merge-gql-context.test.js → merge-gql-context.test.ts} +5 -6
- package/src/util/__tests__/{purge-caches.test.js → purge-caches.test.ts} +3 -4
- package/src/util/__tests__/{request-api.test.js → request-api.test.ts} +5 -5
- package/src/util/__tests__/{request-fulfillment.test.js → request-fulfillment.test.ts} +2 -3
- package/src/util/__tests__/{request-tracking.test.js → request-tracking.test.tsx} +15 -8
- package/src/util/__tests__/{result-from-cache-response.test.js → result-from-cache-response.test.ts} +3 -5
- package/src/util/__tests__/{scoped-in-memory-cache.test.js → scoped-in-memory-cache.test.ts} +5 -6
- package/src/util/__tests__/{serializable-in-memory-cache.test.js → serializable-in-memory-cache.test.ts} +8 -8
- package/src/util/__tests__/{ssr-cache.test.js → ssr-cache.test.ts} +5 -4
- package/src/util/__tests__/{to-gql-operation.test.js → to-gql-operation.test.ts} +5 -4
- package/src/util/{data-error.js → data-error.ts} +3 -4
- package/src/util/{get-gql-data-from-response.js → get-gql-data-from-response.ts} +3 -8
- package/src/util/{get-gql-request-id.js → get-gql-request-id.ts} +13 -17
- package/src/util/{gql-error.js → gql-error.ts} +3 -4
- package/src/util/gql-router-context.ts +6 -0
- package/src/util/{gql-types.js → gql-types.ts} +27 -23
- package/src/util/{graphql-document-node-parser.js → graphql-document-node-parser.ts} +8 -9
- package/src/util/graphql-types.ts +27 -0
- package/src/util/{hydration-cache-api.js → hydration-cache-api.ts} +6 -4
- package/src/util/{merge-gql-context.js → merge-gql-context.ts} +3 -3
- package/src/util/{purge-caches.js → purge-caches.ts} +2 -3
- package/src/util/{request-api.js → request-api.ts} +4 -5
- package/src/util/{request-fulfillment.js → request-fulfillment.ts} +15 -14
- package/src/util/{request-tracking.js → request-tracking.ts} +15 -16
- package/src/util/{result-from-cache-response.js → result-from-cache-response.ts} +6 -7
- package/src/util/{scoped-in-memory-cache.js → scoped-in-memory-cache.ts} +3 -4
- package/src/util/{serializable-in-memory-cache.js → serializable-in-memory-cache.ts} +5 -6
- package/src/util/{ssr-cache.js → ssr-cache.ts} +21 -20
- package/src/util/{status.js → status.ts} +5 -6
- package/src/util/{to-gql-operation.js → to-gql-operation.ts} +4 -5
- package/src/util/{types.js → types.ts} +41 -49
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/src/__docs__/_overview_.stories.mdx +0 -18
- package/src/__docs__/_overview_graphql.stories.mdx +0 -35
- package/src/__docs__/_overview_ssr_.stories.mdx +0 -185
- package/src/__docs__/_overview_testing_.stories.mdx +0 -123
- package/src/__docs__/exports.abort-inflight-requests.stories.mdx +0 -20
- package/src/__docs__/exports.data-error.stories.mdx +0 -23
- package/src/__docs__/exports.data-errors.stories.mdx +0 -23
- package/src/__docs__/exports.data.stories.mdx +0 -146
- package/src/__docs__/exports.fetch-tracked-requests.stories.mdx +0 -24
- package/src/__docs__/exports.get-gql-request-id.stories.mdx +0 -24
- package/src/__docs__/exports.gql-error.stories.mdx +0 -23
- package/src/__docs__/exports.gql-errors.stories.mdx +0 -20
- package/src/__docs__/exports.gql-router.stories.mdx +0 -29
- package/src/__docs__/exports.has-tracked-requests-to-be-fetched.stories.mdx +0 -20
- package/src/__docs__/exports.intercept-requests.stories.mdx +0 -69
- package/src/__docs__/exports.intialize-hydration-cache.stories.mdx +0 -29
- package/src/__docs__/exports.purge-caches.stories.mdx +0 -23
- package/src/__docs__/exports.purge-hydration-cache.stories.mdx +0 -24
- package/src/__docs__/exports.scoped-in-memory-cache.stories.mdx +0 -92
- package/src/__docs__/exports.serializable-in-memory-cache.stories.mdx +0 -112
- package/src/__docs__/exports.shared-cache.stories.mdx +0 -16
- package/src/__docs__/exports.status.stories.mdx +0 -31
- package/src/__docs__/exports.track-data.stories.mdx +0 -209
- package/src/__docs__/exports.use-cached-effect.stories.mdx +0 -44
- package/src/__docs__/exports.use-gql.stories.mdx +0 -41
- package/src/__docs__/exports.use-hydratable-effect.stories.mdx +0 -43
- package/src/__docs__/exports.use-server-effect.stories.mdx +0 -50
- package/src/__docs__/exports.use-shared-cache.stories.mdx +0 -30
- package/src/__docs__/exports.when-client-side.stories.mdx +0 -33
- package/src/__docs__/types.cached-response.stories.mdx +0 -29
- package/src/__docs__/types.error-options.stories.mdx +0 -21
- package/src/__docs__/types.fetch-policy.stories.mdx +0 -44
- package/src/__docs__/types.gql-context.stories.mdx +0 -20
- package/src/__docs__/types.gql-fetch-fn.stories.mdx +0 -24
- package/src/__docs__/types.gql-fetch-options.stories.mdx +0 -24
- package/src/__docs__/types.gql-operation-type.stories.mdx +0 -24
- package/src/__docs__/types.gql-operation.stories.mdx +0 -67
- package/src/__docs__/types.raw-scoped-cache.stories.mdx +0 -27
- package/src/__docs__/types.response-cache.stories.mdx +0 -33
- package/src/__docs__/types.result.stories.mdx +0 -39
- package/src/__docs__/types.scoped-cache.stories.mdx +0 -114
- package/src/__docs__/types.valid-cache-data.stories.mdx +0 -23
- package/src/index.js +0 -55
- package/src/util/gql-router-context.js +0 -6
- package/src/util/graphql-types.js +0 -30
- /package/src/hooks/__tests__/__snapshots__/{use-shared-cache.test.js.snap → use-shared-cache.test.ts.snap} +0 -0
- /package/src/util/__tests__/__snapshots__/{scoped-in-memory-cache.test.js.snap → scoped-in-memory-cache.test.ts.snap} +0 -0
- /package/src/util/__tests__/__snapshots__/{serializable-in-memory-cache.test.js.snap → serializable-in-memory-cache.test.ts.snap} +0 -0
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Server-side Rendering and Hydration"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# Server-side Rendering (SSR)
|
|
13
|
-
|
|
14
|
-
Wonder Blocks Data provides components, hooks, and additional APIs to make server-side rendering and hydration of asynchronous requests easier.
|
|
15
|
-
|
|
16
|
-
The [`Data`](/docs/data-exports-data--page) component and the [`useHydratableEffect`](/docs/data-exports-usehydratableeffect--page) hook (which [`Data`](/docs/data-exports-data--page) uses internally) both
|
|
17
|
-
support server-side rendering and hydration.
|
|
18
|
-
|
|
19
|
-
These APIs have been designed to make it easy for everyday development. The developer does not need to consider the server-side process nor hydration when requiring asynchronous data. Instead, they use [`Data`](/docs/data-exports-data--page) or [`useHydratableEffect`](/docs/data-exports-usehydratableeffect--page) to get the data they need.
|
|
20
|
-
|
|
21
|
-
## Integrating into server-side rendering
|
|
22
|
-
|
|
23
|
-
Generally, server-side rendering has a loop that renders the page, determines what asynchronous activity needs to be resolved, resolves and caches that activity, then renders again, ensuring that as much of the page as possible has been rendered.
|
|
24
|
-
|
|
25
|
-
In order for the requests made through [`Data`](/docs/data-exports-data--page) and [`useHydratableEffect`](/docs/data-exports-usehydratableeffect--page) use to be fulfilled on the server and subsequently hydrated on the client, the server responsible for rendering the page must be modified to track and capture the requests and their responses.
|
|
26
|
-
|
|
27
|
-
First, Wonder Blocks Data has to be told to work in server-side mode:
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
import {Server} from "@khanacademy/wonder-blocks-core";
|
|
31
|
-
|
|
32
|
-
Server.setServerSide();
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
By setting Wonder Blocks into server mode, requests will not be fulfilled during rendering (there would be no point since server-side rendering does not mount components and as such, cannot update the rendered component tree with the result of asynchronous requests). Instead, we are now ready to track these requests.
|
|
36
|
-
|
|
37
|
-
### Tracking and fulfilling requests
|
|
38
|
-
|
|
39
|
-
To track the requests, the React node being rendered must be wrapped in the [`TrackData`](/docs/data-exports-trackdata--page) component.
|
|
40
|
-
|
|
41
|
-
```tsx
|
|
42
|
-
const trackedElement = (
|
|
43
|
-
<TrackData>
|
|
44
|
-
{theNodeTheClientNormallyRenders}
|
|
45
|
-
</TrackData>
|
|
46
|
-
);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
The [`TrackData`](/docs/data-exports-trackdata--page) component is responsible for capturing the requests that were made during a render of the given React node. Of course to do that, we have to render the node. Rendering is usually performed by `renderToString` from the `react-dom/server` import.
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
import {renderToString} from "react-dom/server";
|
|
53
|
-
|
|
54
|
-
renderToString(trackedElement);
|
|
55
|
-
```
|
|
56
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
```ts
|
|
62
|
-
if (hasTrackedRequestsToBeFetched()) {
|
|
63
|
-
await fetchTrackedRequests();
|
|
64
|
-
|
|
65
|
-
// Render again.
|
|
66
|
-
// ...
|
|
67
|
-
} else {
|
|
68
|
-
// Finish rendering the page.
|
|
69
|
-
// ...
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
77
|
-
### Putting it all together
|
|
78
|
-
|
|
79
|
-
> 😱 Make sure you safely embed strings in your rendered pages to avoid injection bugs. This example is brief for the purposes of illustration.
|
|
80
|
-
|
|
81
|
-
A function for server-side rendering with Wonder Blocks Data could look something like this.
|
|
82
|
-
|
|
83
|
-
```tsx
|
|
84
|
-
import {Server} from "@khanacademy/wonder-blocks-core";
|
|
85
|
-
import {
|
|
86
|
-
TrackData,
|
|
87
|
-
hasTrackedRequestsToBeFetched,
|
|
88
|
-
fetchTrackedRequests,
|
|
89
|
-
SharedCache,
|
|
90
|
-
} from "@khanacademy/wonder-blocks-data";
|
|
91
|
-
|
|
92
|
-
// Don't forget to import your app!
|
|
93
|
-
import App from "./App.js";
|
|
94
|
-
|
|
95
|
-
async function renderApp(): Promise<string> {
|
|
96
|
-
// Tell Wonder Blocks we are server-side.
|
|
97
|
-
Server.setServerSide();
|
|
98
|
-
|
|
99
|
-
// Wrap the app with our tracking code.
|
|
100
|
-
const trackedElement = (
|
|
101
|
-
<TrackData>
|
|
102
|
-
<App />
|
|
103
|
-
</TrackData>
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
// Now, loop around rendering and fulfilling until we have nothing left
|
|
107
|
-
// to fulfill.
|
|
108
|
-
let hydrationCache = null;
|
|
109
|
-
let renderedComponent = null;
|
|
110
|
-
do {
|
|
111
|
-
/*
|
|
112
|
-
* Each render has to be isolated, so we have to make sure we clear the
|
|
113
|
-
* shared cache used by the `useSharedCache` hook as this is transient
|
|
114
|
-
* cache that does not itself get directly hydrated.
|
|
115
|
-
*/
|
|
116
|
-
SharedCache.purgeAll();
|
|
117
|
-
|
|
118
|
-
// Render the tracked component.
|
|
119
|
-
renderedComponent = renderToString(trackedElement);
|
|
120
|
-
|
|
121
|
-
if (hasTrackedRequestsToBeFetched()) {
|
|
122
|
-
// Fulfill any pending requests.
|
|
123
|
-
await fetchTrackedRequests();
|
|
124
|
-
|
|
125
|
-
// We can't leave the loop yet as we want to render the element
|
|
126
|
-
// again with the newly fulfilled data.
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
// We rendered with all the data fulfilled, so we can grab the
|
|
132
|
-
// hydration cache contents and exit the loop now.
|
|
133
|
-
hydrationCache = await fetchTrackedRequests();
|
|
134
|
-
} while (hydrationCache == null);
|
|
135
|
-
|
|
136
|
-
// Finally, render the page with our hydration cache and rendered
|
|
137
|
-
// component.
|
|
138
|
-
return `
|
|
139
|
-
<html>
|
|
140
|
-
<head>
|
|
141
|
-
<title>My Page</title>
|
|
142
|
-
<script>
|
|
143
|
-
window.__WONDER_BLOCKS_DATA__ = ${safeStringify(hydrationCache)}
|
|
144
|
-
</script>
|
|
145
|
-
<script src="hydrate.js"></script>
|
|
146
|
-
</head>
|
|
147
|
-
<body>
|
|
148
|
-
<div id="my-react-app-root">
|
|
149
|
-
${renderedComponent}
|
|
150
|
-
</div>
|
|
151
|
-
</body>
|
|
152
|
-
</html>
|
|
153
|
-
`;
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
The above page is simplified for clarity - you would need to make sure all your scripts, fonts, etc. get loaded into the page. However, assuming all those things get loaded, the last piece need is signified above as the script tag importing `hydrate.js` - this is the script that will hydrate the client-side rendered component.
|
|
158
|
-
|
|
159
|
-
## Hydrating on the client-side
|
|
160
|
-
|
|
161
|
-
Now that you have a rendered page, the client needs to be written to properly hydrate to the exact same state as the server rendered. To do this, the hydration cache that was serialized into the page as `window.__WONDER_BLOCKS_DATA__` must be injected into the Wonder Blocks Data cache before the React `hydrate` call is made.
|
|
162
|
-
|
|
163
|
-
In the previous section, this was displayed as the `hydrate.js` script. Here is what that script might look like.
|
|
164
|
-
|
|
165
|
-
```tsx
|
|
166
|
-
import {hydrate} from "react-dom";
|
|
167
|
-
import {initializeHydrationCache} from "@khanacademy/wonder-blocks-data";
|
|
168
|
-
|
|
169
|
-
// Don't forget to import your app!
|
|
170
|
-
import App from "./App.js";
|
|
171
|
-
|
|
172
|
-
initializeHydrationCache(window._WONDER_BLOCKS_DATA_);
|
|
173
|
-
|
|
174
|
-
React.hydrate(
|
|
175
|
-
// This should match whatever was passed to the server-side rendering
|
|
176
|
-
// function, renderApp, in the previous section.
|
|
177
|
-
<App />,
|
|
178
|
-
|
|
179
|
-
// The ID of the element to host the hydrated component has to match what
|
|
180
|
-
// the server-rendered page uses.
|
|
181
|
-
document.getElementById("my-react-app-root"),
|
|
182
|
-
);
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
That's it! The client-side hydration is complete. Underneath the hood, the hydration cache is used to hydrate the responses that the server used to render the page. And if you render a page that was not server-side rendered, the [`Data`](/docs/data-exports-data--page) and [`useHydratableEffect`](/docs/data-exports-usehydratableeffect--page) know exactly what to do and will make the requests client-side instead.
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Testing"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# Testing Support
|
|
13
|
-
|
|
14
|
-
Wonder Blocks Data has been designed to support testing in a variety of environments including jest and storybook. In order to support the various ways in which folks need to test their code, we have considered a number of approaches to testing Wonder Blocks Data.
|
|
15
|
-
|
|
16
|
-
## Spies
|
|
17
|
-
If you are writing unit tests, you may just want to spy on the methods you are calling using `jest.spyOn` or similar. This can be a really easy way to intercept the request handler passed to the [`useCachedEffect`](/docs/data-exports-usecachedeffect--page) hook, for example, and check that it is the handler you expect.
|
|
18
|
-
|
|
19
|
-
## Interceptors
|
|
20
|
-
|
|
21
|
-
Each request used by Wonder Blocks Data has to have an identifier. The [`InterceptRequests`](/docs/data-exports-interceptrequests--page) component allows you to wrap the code under test with an interceptor. Interceptors are given the request identifier and get to choose, based off that identifier, if they want to provide their own response rather than let the original request handler deal with it.
|
|
22
|
-
|
|
23
|
-
Multiple interceptors can be registered by nesting the [`InterceptRequests`](/docs/data-exports-interceptrequests--page) component as is necessary. Registered interceptors are invoked in ancestral order, with the nearest ancestor to the intercepted request being invoked first.
|
|
24
|
-
|
|
25
|
-
When hooks like [`useServerEffect`](/docs/data-exports-useservereffect--page), [`useCachedEffect`](/docs/data-exports-usecachedeffect--page), or [`useHydratedEffect`](/docs/data-exports-usehydratedeffect--page) run, they get the chain of registered interceptors and chain those with the original handler in order to determine what to actually do when executing the request.
|
|
26
|
-
|
|
27
|
-
This allows you to mock out requests in unit tests, stories, and other scenarios.
|
|
28
|
-
|
|
29
|
-
## GqlRouter, mockGqlFetch, and RespondWith
|
|
30
|
-
|
|
31
|
-
If you are testing GraphQL operations, you can configure [`GqlRouter`](/docs/data-exports-gqlrouter--page) with your own function for the `fetch` prop. However, crafting the right response to give
|
|
32
|
-
the result you want is a bit tricky.
|
|
33
|
-
|
|
34
|
-
```tsx
|
|
35
|
-
const myFakeGqlFetch = (
|
|
36
|
-
operation: GqlOperation<TData, TVariables>,
|
|
37
|
-
variables: ?TVariables,
|
|
38
|
-
context: TContext,
|
|
39
|
-
): Promise<Response> {
|
|
40
|
-
if (operation.id === "myQuery" && variables?.someVar === 5) {
|
|
41
|
-
return Promise.resolve({
|
|
42
|
-
status: 200,
|
|
43
|
-
text: () =>
|
|
44
|
-
Promise.resolve(
|
|
45
|
-
JSON.stringify({
|
|
46
|
-
data: {
|
|
47
|
-
myQuery: {
|
|
48
|
-
someField: "someValue",
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
}),
|
|
52
|
-
),
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return Promise.resolve({
|
|
57
|
-
status: 404,
|
|
58
|
-
text: () => Promise.resolve(JSON.stringify({})),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
<GqlRouter fetch={myFakeGqlFetch} defaultContext={{some: "sort of context"}}>
|
|
63
|
-
<ComponentUnderTest />
|
|
64
|
-
</GqlRouter>
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
As shown above, you can interrogate parts of the requested operation to decide how to respond. However, this can get cumbersome if you have GraphQL requests nested in more complex components as each test has to mock out suitable responses for each one. To help with this the Wonder Blocks Testing package provides a [`RespondWith`](/docs/testing-exports-respondwith--page) type for defining responses that fit a specific scenario, and the [`mockGqlFetch()`](/docs/testing-exports-mockgqlfetch--page) API.
|
|
68
|
-
|
|
69
|
-
```tsx
|
|
70
|
-
const myFakeGqlFetch = mockGqlFetch()
|
|
71
|
-
.mockOperationOnce(
|
|
72
|
-
{
|
|
73
|
-
operation: MyQueryOperation,
|
|
74
|
-
variables: {
|
|
75
|
-
someVar: 5,
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
RespondWith.success({
|
|
79
|
-
data: {
|
|
80
|
-
myQuery: {
|
|
81
|
-
someField: "someValue",
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
}),
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
<GqlRouter fetch={myFakeGqlFetch} defaultContext={{some: "sort of context"}}>
|
|
88
|
-
<ComponentUnderTest />
|
|
89
|
-
</GqlRouter>
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
In the above example, we now only mock our specific operation once. If something
|
|
93
|
-
tries to request this data a second time, it will give an error instead. Not only that, but with a little refactoring, we can create a helper to set this mock up that others can call if they need to mock our operation for their own tests.
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
const mockMyQuery = (mockGqlFetchFn: GqlFetchMockFn): GqlFetchMockFn =>
|
|
97
|
-
mockGqlFetchFn()
|
|
98
|
-
.mockOperationOnce(
|
|
99
|
-
{
|
|
100
|
-
operation: MyQueryOperation,
|
|
101
|
-
variables: {
|
|
102
|
-
someVar: 5,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
RespondWith.success({
|
|
106
|
-
data: {
|
|
107
|
-
myQuery: {
|
|
108
|
-
someField: "someValue",
|
|
109
|
-
},
|
|
110
|
-
},
|
|
111
|
-
}),
|
|
112
|
-
);
|
|
113
|
-
|
|
114
|
-
const myFakeGqlFetch = mockMyQuery(mockGqlFetch());
|
|
115
|
-
|
|
116
|
-
<GqlRouter fetch={myFakeGqlFetch} defaultContext={{some: "sort of context"}}>
|
|
117
|
-
<ComponentUnderTest />
|
|
118
|
-
</GqlRouter>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Now, using a compose function, multiple mocks can be setup on the same `mockGqlFetch` instance.
|
|
122
|
-
|
|
123
|
-
For more details on this and other testing utilities, see the [Wonder Blocks Testing documentation](/docs/testing-overview--page).
|
|
@@ -1,20 +0,0 @@
|
|
|
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.
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Exports / DataError"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# DataError
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
new DataError(
|
|
17
|
-
message: string,
|
|
18
|
-
kind: $Values<typeof DataErrors>,
|
|
19
|
-
options?: ErrorOptions,
|
|
20
|
-
);
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
The `DataError` class is a derivation of the Wonder Blocks Core `KindError` (which is itself a derivation of `Error`). It is used by the Wonder Blocks Data framework to encapsulate errors that can occur when using its API. The different kinds of errors supported are defined by the [`DataErrors`](/docs/data-exports-dataerrors--page) export.
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Exports / DataErrors"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# DataErrors
|
|
13
|
-
|
|
14
|
-
This export defines the error taxonomy used by Wonder Blocks Data to describe the errors that can occur. These are for use with [`DataError`](/docs/data-exports-dataerror--page).
|
|
15
|
-
|
|
16
|
-
| Kind | Description |
|
|
17
|
-
| ---- | ----------- |
|
|
18
|
-
| `DataErrors.Unknown` | The kind of error is not known. |
|
|
19
|
-
| `DataErrors.Internal` | The error is internal to the executing code. |
|
|
20
|
-
| `DataErrors.InvalidInput` | There was a problem with the provided input. |
|
|
21
|
-
| `DataErrors.Network` | A network error occurred. |
|
|
22
|
-
| `DataErrors.Parse` | Response could not be parsed. |
|
|
23
|
-
| `DataErrors.Hydrated` | An error that occurred during SSR and was hydrated from cache |
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
import {Data} from "../index.js";
|
|
3
|
-
|
|
4
|
-
<Meta
|
|
5
|
-
title="Data / Exports / Data"
|
|
6
|
-
component={Data}
|
|
7
|
-
parameters={{
|
|
8
|
-
chromatic: {
|
|
9
|
-
disableSnapshot: true,
|
|
10
|
-
},
|
|
11
|
-
}}
|
|
12
|
-
/>
|
|
13
|
-
|
|
14
|
-
# Data
|
|
15
|
-
|
|
16
|
-
The `Data` component is the frontend piece of our data architecture.
|
|
17
|
-
It describes a data requirement in terms of a handler and an identifier.
|
|
18
|
-
It also has props to govern hydrate behavior as well as loading and client-side
|
|
19
|
-
request behavior.
|
|
20
|
-
|
|
21
|
-
The handler is responsible for fulfilling the request when asked to do so.
|
|
22
|
-
|
|
23
|
-
#### Server-side Rendering and Hydration
|
|
24
|
-
|
|
25
|
-
The Wonder Blocks Data framework uses an in-memory cache for supporting
|
|
26
|
-
server-side rendering (SSR) and hydration.
|
|
27
|
-
|
|
28
|
-
##### Server-side behavior
|
|
29
|
-
|
|
30
|
-
###### Cache miss
|
|
31
|
-
|
|
32
|
-
When the `Data` component does not get data or an error from the cache and it
|
|
33
|
-
is rendering server-side, it tells our request tracking that it wants data, and
|
|
34
|
-
it renders in its `loading` state. It will always render in this state if there
|
|
35
|
-
is no cached response.
|
|
36
|
-
|
|
37
|
-
###### Cache hit
|
|
38
|
-
|
|
39
|
-
When the `Data` component gets data or an error from the cache and it is
|
|
40
|
-
rendering server-side, it will render as loaded, with that data or error,
|
|
41
|
-
as it would client-side. In this situation, it does not track the request it
|
|
42
|
-
would have made, as it already has the data and doesn't need to.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
##### Client-side behavior
|
|
46
|
-
|
|
47
|
-
###### Cache miss
|
|
48
|
-
|
|
49
|
-
When the hydration cache does not contain the data, the data will be requested.
|
|
50
|
-
While the request is pending, the data is rendered in the loading state.
|
|
51
|
-
In this example, we use a 3 second delayed promise to simulate the request.
|
|
52
|
-
We start out without any data and so the request is made. Upon receipt of that
|
|
53
|
-
data or an error, we re-render.
|
|
54
|
-
|
|
55
|
-
```jsx
|
|
56
|
-
import {Body, BodyMonospace} from "@khanacademy/wonder-blocks-typography";
|
|
57
|
-
import {View} from "@khanacademy/wonder-blocks-core";
|
|
58
|
-
import {Data} from "@khanacademy/wonder-blocks-data";
|
|
59
|
-
import {Strut} from "@khanacademy/wonder-blocks-layout";
|
|
60
|
-
import Color from "@khanacademy/wonder-blocks-color";
|
|
61
|
-
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
62
|
-
|
|
63
|
-
const myValidHandler = () => new Promise((resolve, reject) =>
|
|
64
|
-
setTimeout(() => resolve("I'm DATA from a request"), 3000),
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
const myInvalidHandler = () => new Promise((resolve, reject) =>
|
|
68
|
-
setTimeout(() => reject("I'm an ERROR from a request"), 3000),
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
<View>
|
|
72
|
-
<View>
|
|
73
|
-
<Body>This request will succeed and give us data!</Body>
|
|
74
|
-
<Data handler={myValidHandler} requestId="VALID">
|
|
75
|
-
{(result) => {
|
|
76
|
-
if (result.status === "loading") {
|
|
77
|
-
return "Loading...";
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<BodyMonospace>{result.data}</BodyMonospace>
|
|
82
|
-
);
|
|
83
|
-
}}
|
|
84
|
-
</Data>
|
|
85
|
-
</View>
|
|
86
|
-
<Strut size={Spacing.small_12} />
|
|
87
|
-
<View>
|
|
88
|
-
<Body>This request will go boom and give us an error!</Body>
|
|
89
|
-
<Data handler={myInvalidHandler} requestId="INVALID">
|
|
90
|
-
{(result) => {
|
|
91
|
-
if (result.status === "loading") {
|
|
92
|
-
return "Loading...";
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<BodyMonospace style={{color: Color.red}}>ERROR: {result.error}</BodyMonospace>
|
|
97
|
-
);
|
|
98
|
-
}}
|
|
99
|
-
</Data>
|
|
100
|
-
</View>
|
|
101
|
-
</View>
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
###### Cache hit
|
|
105
|
-
|
|
106
|
-
If the hydration cache already contains data or an error for our request, then
|
|
107
|
-
the `Data` component will render it immediately. The hydration cache is
|
|
108
|
-
populated using the `initializeHydrationCache` method before rendering.
|
|
109
|
-
|
|
110
|
-
```jsx
|
|
111
|
-
import {Body, BodyMonospace} from "@khanacademy/wonder-blocks-typography";
|
|
112
|
-
import {View} from "@khanacademy/wonder-blocks-core";
|
|
113
|
-
import {Data, initializeHydrationCache} from "@khanacademy/wonder-blocks-data";
|
|
114
|
-
import {Strut} from "@khanacademy/wonder-blocks-layout";
|
|
115
|
-
import Color from "@khanacademy/wonder-blocks-color";
|
|
116
|
-
import Spacing from "@khanacademy/wonder-blocks-spacing";
|
|
117
|
-
|
|
118
|
-
const myHandler = () => {
|
|
119
|
-
throw new Error(
|
|
120
|
-
"If you're seeing this error, the examples are broken and data isn't in the cache that should be.",
|
|
121
|
-
);
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
initializeHydrationCache({
|
|
125
|
-
DATA: {
|
|
126
|
-
data: "I'm DATA from the hydration cache"
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
<View>
|
|
131
|
-
<View>
|
|
132
|
-
<Body>This cache has data!</Body>
|
|
133
|
-
<Data handler={myHandler} requestId="DATA">
|
|
134
|
-
{(result) => {
|
|
135
|
-
if (result.status !== "success") {
|
|
136
|
-
return "If you see this, the example is broken!";
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return (
|
|
140
|
-
<BodyMonospace>{result.data}</BodyMonospace>
|
|
141
|
-
);
|
|
142
|
-
}}
|
|
143
|
-
</Data>
|
|
144
|
-
</View>
|
|
145
|
-
</View>
|
|
146
|
-
```
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Exports / fetchTrackedRequests()"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# fetchTrackedRequests()
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
fetchTrackedRequests(): Promise<ResponseCache>;
|
|
16
|
-
```
|
|
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 `fetchTrackedRequests`.
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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).
|
|
@@ -1,24 +0,0 @@
|
|
|
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,23 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Exports / GqlError"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# GqlError
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
new GqlError(
|
|
17
|
-
message: string,
|
|
18
|
-
kind: $Values<typeof GqlErrors>,
|
|
19
|
-
options?: ErrorOptions,
|
|
20
|
-
);
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
The `GqlError` class is a derivation of the Wonder Blocks Core `KindError` (which is itself a derivation of `Error`). It is used by the Wonder Blocks Data GraphQL framework to encapsulate errors that can occur within the GraphQL API. The different kinds of errors supported are defined by the [`GqlErrors`](/docs/data-exports-gqlerrors--page) export.
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Exports / GqlErrors"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# GqlErrors
|
|
13
|
-
|
|
14
|
-
This export defines the error taxonomy used by Wonder Blocks Data to describe the GraphQL-related errors that can occur with GraphQL requests. These are for use with [`GqlError`](/docs/data-exports-gqlerror--page).
|
|
15
|
-
|
|
16
|
-
| Kind | Description |
|
|
17
|
-
| ---- | ----------- |
|
|
18
|
-
| `GqlErrors.Unknown` | The type of error is unknown. |
|
|
19
|
-
| `GqlErrors.BadResponse` | Response does not have the correct structure for a GraphQL response. |
|
|
20
|
-
| `GqlErrors.ErrorResult` | A valid GraphQL result with errors field in the payload. |
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
import {GqlRouter} from "../index.js";
|
|
3
|
-
|
|
4
|
-
<Meta
|
|
5
|
-
title="Data / Exports / GqlRouter"
|
|
6
|
-
component={GqlRouter}
|
|
7
|
-
parameters={{
|
|
8
|
-
chromatic: {
|
|
9
|
-
disableSnapshot: true,
|
|
10
|
-
},
|
|
11
|
-
}}
|
|
12
|
-
/>
|
|
13
|
-
|
|
14
|
-
# GqlRouter
|
|
15
|
-
|
|
16
|
-
The `GqlRouter` component is used to define the default context and fetch function for performing GraphQL requests using the Wonder Blocks Data API.
|
|
17
|
-
|
|
18
|
-
The [`useGql`](/docs/data-exports-usegql--page) (and therefore any hooks or components that use it) requires at least one `GqlRouter` in the component hierarchy above it or it will throw an error.
|
|
19
|
-
|
|
20
|
-
The `defaultContext` is specific to the needs of your specific implementation, as is the `fetch` function. Currently, a default for the fetch function is not provided by Wonder Blocks Data and should be written to the specification of the server that will fulfill your GraphQL responses. The `fetch` function must conform to the [`GqlFetchFn<>`](/docs/data-types-gqlfetchfn--page) type.
|
|
21
|
-
|
|
22
|
-
```tsx
|
|
23
|
-
<GqlRouter defaultContext={{a: "1", b: "2"}} fetch={gqlFetchFn}>
|
|
24
|
-
<div>
|
|
25
|
-
The code that will ultimately use Wonder Blocks Data-based GraphQL
|
|
26
|
-
operations goes here.
|
|
27
|
-
</div>
|
|
28
|
-
</GqlRouter>
|
|
29
|
-
```
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {Meta} from "@storybook/addon-docs";
|
|
2
|
-
|
|
3
|
-
<Meta
|
|
4
|
-
title="Data / Exports / hasTrackedRequestsToBeFetched()"
|
|
5
|
-
parameters={{
|
|
6
|
-
chromatic: {
|
|
7
|
-
disableSnapshot: true,
|
|
8
|
-
},
|
|
9
|
-
}}
|
|
10
|
-
/>
|
|
11
|
-
|
|
12
|
-
# hasTrackedRequestsToBeFetched()
|
|
13
|
-
|
|
14
|
-
```ts
|
|
15
|
-
hasTrackedRequestsToBeFetched(): boolean;
|
|
16
|
-
```
|
|
17
|
-
|
|
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
|
-
|
|
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).
|