@khanacademy/wonder-blocks-data 6.0.0 → 7.0.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/dist/index.js CHANGED
@@ -82,7 +82,7 @@ module.exports =
82
82
  /******/
83
83
  /******/
84
84
  /******/ // Load entry module and return exports
85
- /******/ return __webpack_require__(__webpack_require__.s = 28);
85
+ /******/ return __webpack_require__(__webpack_require__.s = 27);
86
86
  /******/ })
87
87
  /************************************************************************/
88
88
  /******/ ([
@@ -885,11 +885,9 @@ class RequestFulfillment {
885
885
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "b", function() { return useHydratableEffect; });
886
886
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
887
887
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
888
- /* harmony import */ var _util_abort_error_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(24);
889
- /* harmony import */ var _use_server_effect_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(14);
890
- /* harmony import */ var _use_shared_cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
891
- /* harmony import */ var _use_cached_effect_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(15);
892
-
888
+ /* harmony import */ var _use_server_effect_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14);
889
+ /* harmony import */ var _use_shared_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8);
890
+ /* harmony import */ var _use_cached_effect_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(15);
893
891
 
894
892
 
895
893
 
@@ -898,7 +896,7 @@ class RequestFulfillment {
898
896
  /**
899
897
  * Policies to define how a hydratable effect should behave client-side.
900
898
  */
901
- const WhenClientSide = __webpack_require__(29).Mirrored(["DoNotHydrate", "ExecuteWhenNoResult", "ExecuteWhenNoSuccessResult", "AlwaysExecute"]);
899
+ const WhenClientSide = __webpack_require__(28).Mirrored(["DoNotHydrate", "ExecuteWhenNoResult", "ExecuteWhenNoSuccessResult", "AlwaysExecute"]);
902
900
  const DefaultScope = "useHydratableEffect";
903
901
  /**
904
902
  * Hook to execute an async operation on server and client.
@@ -922,10 +920,11 @@ const useHydratableEffect = (requestId, handler, options = {}) => {
922
920
  // When client-side, this will look up any response for hydration; it does
923
921
  // not invoke the handler.
924
922
 
925
- const serverResult = Object(_use_server_effect_js__WEBPACK_IMPORTED_MODULE_2__[/* useServerEffect */ "a"])(requestId, // If we're skipped (unlikely in server worlds, but maybe),
926
- // just give an aborted response.
927
- skip ? () => Promise.reject(new _util_abort_error_js__WEBPACK_IMPORTED_MODULE_1__[/* AbortError */ "a"]("skipped")) : handler, // Only hydrate if our behavior isn't telling us not to.
928
- clientBehavior !== WhenClientSide.DoNotHydrate);
923
+ const serverResult = Object(_use_server_effect_js__WEBPACK_IMPORTED_MODULE_1__[/* useServerEffect */ "a"])(requestId, handler, {
924
+ // Only hydrate if our behavior isn't telling us not to.
925
+ hydrate: clientBehavior !== WhenClientSide.DoNotHydrate,
926
+ skip
927
+ });
929
928
  const getDefaultCacheValue = react__WEBPACK_IMPORTED_MODULE_0__["useCallback"](() => {
930
929
  // If we don't have a requestId, it's our first render, the one
931
930
  // where we hydrated. So defer to our clientBehavior value.
@@ -952,17 +951,24 @@ const useHydratableEffect = (requestId, handler, options = {}) => {
952
951
  }
953
952
 
954
953
  return null;
955
- } // There is no reason for this to change after the first render.
954
+ } // There is no reason for this to change after the first render,
955
+ // you might think, but the function closes around serverResult and if
956
+ // the requestId changes, it still returns the hydrate result of the
957
+ // first render of the previous requestId. This then means that the
958
+ // hydrate result is still the same, and the effect is not re-executed
959
+ // because the cache gets incorrectly defaulted.
960
+ // However, we don't want to bother doing anything with this on
961
+ // client behavior changing since that truly is irrelevant.
956
962
  // eslint-disable-next-line react-hooks/exhaustive-deps
957
963
 
958
- }, []); // Instead of using state, which would be local to just this hook instance,
964
+ }, [serverResult]); // Instead of using state, which would be local to just this hook instance,
959
965
  // we use a shared in-memory cache.
960
966
 
961
- Object(_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_3__[/* useSharedCache */ "b"])(requestId, // The key of the cached item
967
+ Object(_use_shared_cache_js__WEBPACK_IMPORTED_MODULE_2__[/* useSharedCache */ "b"])(requestId, // The key of the cached item
962
968
  scope, // The scope of the cached items
963
969
  getDefaultCacheValue); // When we're client-side, we ultimately want the result from this call.
964
970
 
965
- const clientResult = Object(_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_4__[/* useCachedEffect */ "a"])(requestId, handler, {
971
+ const clientResult = Object(_use_cached_effect_js__WEBPACK_IMPORTED_MODULE_3__[/* useCachedEffect */ "a"])(requestId, handler, {
966
972
  skip,
967
973
  onResultChanged,
968
974
  retainResultOnChange,
@@ -1054,7 +1060,7 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
1054
1060
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
1055
1061
  /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
1056
1062
  /* harmony import */ var _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
1057
- /* harmony import */ var _util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(25);
1063
+ /* harmony import */ var _util_result_from_cache_response_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(24);
1058
1064
  /* harmony import */ var _use_request_interception_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(16);
1059
1065
 
1060
1066
 
@@ -1078,22 +1084,26 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
1078
1084
  *
1079
1085
  * The asynchronous action is never invoked on the client-side.
1080
1086
  */
1081
- const useServerEffect = (requestId, handler, hydrate = true) => {
1082
- // Plug in to the request interception framework for code that wants
1087
+ const useServerEffect = (requestId, handler, options = {}) => {
1088
+ const {
1089
+ hydrate = true,
1090
+ skip = false
1091
+ } = options; // Plug in to the request interception framework for code that wants
1083
1092
  // to use that.
1093
+
1084
1094
  const interceptedHandler = Object(_use_request_interception_js__WEBPACK_IMPORTED_MODULE_5__[/* useRequestInterception */ "a"])(requestId, handler); // If we're server-side or hydrating, we'll have a cached entry to use.
1085
1095
  // So we get that and use it to initialize our state.
1086
1096
  // This works in both hydration and SSR because the very first call to
1087
1097
  // this will have cached data in those cases as it will be present on the
1088
1098
  // initial render - and subsequent renders on the client it will be null.
1089
1099
 
1090
- const cachedResult = _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__[/* SsrCache */ "a"].Default.getEntry(requestId); // We only track data requests when we are server-side and we don't
1091
- // already have a result, as given by the cachedData (which is also the
1092
- // initial value for the result state).
1100
+ const cachedResult = _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__[/* SsrCache */ "a"].Default.getEntry(requestId); // We only track data requests when we are server-side, we are not skipping
1101
+ // the request, and we don't already have a result, as given by the
1102
+ // cachedData (which is also the initial value for the result state).
1093
1103
 
1094
1104
  const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* TrackerContext */ "b"]);
1095
1105
 
1096
- if (cachedResult == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
1106
+ if (!skip && cachedResult == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
1097
1107
  maybeTrack == null ? void 0 : maybeTrack(requestId, interceptedHandler, hydrate);
1098
1108
  } // A null result means there was no result to hydrate.
1099
1109
 
@@ -1510,8 +1520,8 @@ const GqlRouter = ({
1510
1520
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1511
1521
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1512
1522
  /* harmony import */ var _util_merge_gql_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(18);
1513
- /* harmony import */ var _use_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(26);
1514
- /* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(27);
1523
+ /* harmony import */ var _use_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(25);
1524
+ /* harmony import */ var _util_get_gql_data_from_response_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(26);
1515
1525
 
1516
1526
 
1517
1527
 
@@ -1555,27 +1565,6 @@ const useGql = (context = {}) => {
1555
1565
  /* 24 */
1556
1566
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1557
1567
 
1558
- "use strict";
1559
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return AbortError; });
1560
- /**
1561
- * Simple implementation to represent aborting.
1562
- *
1563
- * Other frameworks may provide this too, so we won't be sharing this with
1564
- * the outside world. It's just a utility for test and internal use whenever
1565
- * we need to represent the concept of aborted things.
1566
- */
1567
- class AbortError extends Error {
1568
- constructor(message) {
1569
- super(message);
1570
- this.name = "AbortError";
1571
- }
1572
-
1573
- }
1574
-
1575
- /***/ }),
1576
- /* 25 */
1577
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1578
-
1579
1568
  "use strict";
1580
1569
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return resultFromCachedResponse; });
1581
1570
  /* harmony import */ var _status_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
@@ -1613,7 +1602,7 @@ const resultFromCachedResponse = cacheEntry => {
1613
1602
  };
1614
1603
 
1615
1604
  /***/ }),
1616
- /* 26 */
1605
+ /* 25 */
1617
1606
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1618
1607
 
1619
1608
  "use strict";
@@ -1665,7 +1654,7 @@ const useGqlRouterContext = (contextOverrides = {}) => {
1665
1654
  };
1666
1655
 
1667
1656
  /***/ }),
1668
- /* 27 */
1657
+ /* 26 */
1669
1658
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1670
1659
 
1671
1660
  "use strict";
@@ -1735,7 +1724,7 @@ const getGqlDataFromResponse = async response => {
1735
1724
  };
1736
1725
 
1737
1726
  /***/ }),
1738
- /* 28 */
1727
+ /* 27 */
1739
1728
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1740
1729
 
1741
1730
  "use strict";
@@ -1882,7 +1871,7 @@ const removeAllFromCache = predicate => _util_ssr_cache_js__WEBPACK_IMPORTED_MOD
1882
1871
 
1883
1872
 
1884
1873
  /***/ }),
1885
- /* 29 */
1874
+ /* 28 */
1886
1875
  /***/ (function(module, exports, __webpack_require__) {
1887
1876
 
1888
1877
  "use strict";
package/legacy-docs.md CHANGED
@@ -1,3 +1,3 @@
1
1
  Documentation for Wonder Blocks Data is now in Storybook.
2
2
 
3
- Either run `yarn start:storybook` locally, or visit the the stories for the `master` branch on [Chromatic](https://master--5e1bf4b385e3fb0020b7073c.chromatic.com).
3
+ Either run `yarn start:storybook` locally, or visit the the stories for the `main` branch on [Chromatic](https://main--5e1bf4b385e3fb0020b7073c.chromatic.com).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-data",
3
- "version": "6.0.0",
3
+ "version": "7.0.1",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -14,14 +14,14 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "@babel/runtime": "^7.16.3",
17
- "@khanacademy/wonder-blocks-core": "^4.3.0"
17
+ "@khanacademy/wonder-blocks-core": "^4.3.1"
18
18
  },
19
19
  "peerDependencies": {
20
20
  "@khanacademy/wonder-stuff-core": "^0.1.2",
21
21
  "react": "16.14.0"
22
22
  },
23
23
  "devDependencies": {
24
- "wb-dev-build-settings": "^0.3.0"
24
+ "wb-dev-build-settings": "^0.4.0"
25
25
  },
26
26
  "author": "",
27
27
  "license": "MIT"
@@ -15,12 +15,24 @@ import {Meta} from "@storybook/addon-docs";
15
15
  function useServerEffect<TData: ValidCacheData>(
16
16
  requestId: string,
17
17
  handler: () => Promise<TData>,
18
- hydrate: boolean = true,
18
+ options?: ServerEffectOptions,
19
19
  ): ?Result<TData>;
20
20
  ```
21
21
 
22
22
  The `useServerEffect` hook is an integral part of server-side rendering. It has different behavior depending on whether it is running on the server (and in what context) or the client.
23
23
 
24
+ ```ts
25
+ type ServerEffectOptions = {|
26
+ skip?: boolean,
27
+ hydrate?: boolean,
28
+ |};
29
+ ```
30
+
31
+ | Option | Default | Description |
32
+ | ------ | ------- | ----------- |
33
+ | `hydrate` | `true` | When `true`, the result of the effect when fulfilled using Wonder Blocks Data will be stored in the hydration cache for hydrating client-side; otherwise, the result will be stored in the server-side-only cache. |
34
+ | `skip` | `false` | When `true`, the effect will not be tracked for fulfillment; otherwise, the effect will be tracked for fulfillment. |
35
+
24
36
  ## Server-side behavior
25
37
 
26
38
  First, this hook checks the server-side rendering cache for the request identifier; if it finds a cached value, it will return that.
@@ -107,34 +107,11 @@ describe("#useHydratableEffect", () => {
107
107
  expect(useServerEffectSpy).toHaveBeenCalledWith(
108
108
  "ID",
109
109
  fakeHandler,
110
- hydrate,
110
+ {hydrate, skip: false},
111
111
  );
112
112
  },
113
113
  );
114
114
 
115
- it("should pass an abort handler to useServerEffect when skip is true", async () => {
116
- // Arrange
117
- jest.spyOn(
118
- UseRequestInterception,
119
- "useRequestInterception",
120
- ).mockReturnValue(jest.fn());
121
- const fakeHandler = jest.fn();
122
- const useServerEffectSpy = jest
123
- .spyOn(UseServerEffect, "useServerEffect")
124
- .mockReturnValue(null);
125
-
126
- // Act
127
- serverRenderHook(() =>
128
- useHydratableEffect("ID", fakeHandler, {skip: true}),
129
- );
130
- const underTest = useServerEffectSpy.mock.calls[0][1]();
131
-
132
- // Assert
133
- await expect(underTest).rejects.toMatchInlineSnapshot(
134
- `[AbortError: skipped]`,
135
- );
136
- });
137
-
138
115
  it.each`
139
116
  scope | expectedScope
140
117
  ${undefined} | ${"useHydratableEffect"}
@@ -247,7 +224,7 @@ describe("#useHydratableEffect", () => {
247
224
  expect(useServerEffectSpy).toHaveBeenCalledWith(
248
225
  "ID",
249
226
  fakeHandler,
250
- hydrate,
227
+ {hydrate, skip: false},
251
228
  );
252
229
  },
253
230
  );
@@ -413,6 +390,26 @@ describe("#useHydratableEffect", () => {
413
390
  expect(fakeHandler).toHaveBeenCalledTimes(2);
414
391
  });
415
392
 
393
+ it("should default shared cache to hydrate value for new requestId", async () => {
394
+ // Arrange
395
+ const fakeHandler = jest.fn().mockResolvedValue("data");
396
+ jest.spyOn(UseServerEffect, "useServerEffect")
397
+ .mockReturnValueOnce(Status.success("BADDATA"))
398
+ .mockReturnValue(null);
399
+
400
+ // Act
401
+ const {rerender, result} = clientRenderHook(
402
+ ({requestId}) => useHydratableEffect(requestId, fakeHandler),
403
+ {
404
+ initialProps: {requestId: "ID"},
405
+ },
406
+ );
407
+ rerender({requestId: "ID2"});
408
+
409
+ // Assert
410
+ expect(result.current).toStrictEqual(Status.loading());
411
+ });
412
+
416
413
  it("should update shared cache with result when request is fulfilled", async () => {
417
414
  // Arrange
418
415
  const setCacheFn = jest.fn();
@@ -109,6 +109,57 @@ describe("#useServerEffect", () => {
109
109
  );
110
110
  });
111
111
 
112
+ it("should not track the intercepted request if skip is true", () => {
113
+ // Arrange
114
+ const fakeHandler = jest.fn();
115
+ const interceptedHandler = jest.fn();
116
+ jest.spyOn(
117
+ UseRequestInterception,
118
+ "useRequestInterception",
119
+ ).mockReturnValue(interceptedHandler);
120
+ const trackDataRequestSpy = jest.spyOn(
121
+ RequestTracker.Default,
122
+ "trackDataRequest",
123
+ );
124
+
125
+ // Act
126
+ serverRenderHook(
127
+ () => useServerEffect("ID", fakeHandler, {skip: true}),
128
+ {
129
+ wrapper: TrackData,
130
+ },
131
+ );
132
+
133
+ // Assert
134
+ expect(trackDataRequestSpy).not.toHaveBeenCalled();
135
+ });
136
+
137
+ it("should not track the intercepted request if there is a cached result", () => {
138
+ // Arrange
139
+ const fakeHandler = jest.fn();
140
+ const interceptedHandler = jest.fn();
141
+ jest.spyOn(SsrCache.Default, "getEntry").mockReturnValueOnce({
142
+ data: "DATA",
143
+ error: null,
144
+ });
145
+ jest.spyOn(
146
+ UseRequestInterception,
147
+ "useRequestInterception",
148
+ ).mockReturnValue(interceptedHandler);
149
+ const trackDataRequestSpy = jest.spyOn(
150
+ RequestTracker.Default,
151
+ "trackDataRequest",
152
+ );
153
+
154
+ // Act
155
+ serverRenderHook(() => useServerEffect("ID", fakeHandler), {
156
+ wrapper: TrackData,
157
+ });
158
+
159
+ // Assert
160
+ expect(trackDataRequestSpy).not.toHaveBeenCalled();
161
+ });
162
+
112
163
  it("should return data cached result", () => {
113
164
  // Arrange
114
165
  const fakeHandler = jest.fn();
@@ -1,8 +1,6 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
3
 
4
- import {AbortError} from "../util/abort-error.js";
5
-
6
4
  import {useServerEffect} from "./use-server-effect.js";
7
5
  import {useSharedCache} from "./use-shared-cache.js";
8
6
  import {useCachedEffect} from "./use-cached-effect.js";
@@ -141,16 +139,11 @@ export const useHydratableEffect = <TData: ValidCacheData>(
141
139
  // Now we instruct the server to perform the operation.
142
140
  // When client-side, this will look up any response for hydration; it does
143
141
  // not invoke the handler.
144
- const serverResult = useServerEffect(
145
- requestId,
146
-
147
- // If we're skipped (unlikely in server worlds, but maybe),
148
- // just give an aborted response.
149
- skip ? () => Promise.reject(new AbortError("skipped")) : handler,
150
-
142
+ const serverResult = useServerEffect(requestId, handler, {
151
143
  // Only hydrate if our behavior isn't telling us not to.
152
- clientBehavior !== WhenClientSide.DoNotHydrate,
153
- );
144
+ hydrate: clientBehavior !== WhenClientSide.DoNotHydrate,
145
+ skip,
146
+ });
154
147
 
155
148
  const getDefaultCacheValue: () => ?Result<TData> = React.useCallback(() => {
156
149
  // If we don't have a requestId, it's our first render, the one
@@ -178,9 +171,16 @@ export const useHydratableEffect = <TData: ValidCacheData>(
178
171
  }
179
172
  return null;
180
173
  }
181
- // There is no reason for this to change after the first render.
174
+ // There is no reason for this to change after the first render,
175
+ // you might think, but the function closes around serverResult and if
176
+ // the requestId changes, it still returns the hydrate result of the
177
+ // first render of the previous requestId. This then means that the
178
+ // hydrate result is still the same, and the effect is not re-executed
179
+ // because the cache gets incorrectly defaulted.
180
+ // However, we don't want to bother doing anything with this on
181
+ // client behavior changing since that truly is irrelevant.
182
182
  // eslint-disable-next-line react-hooks/exhaustive-deps
183
- }, []);
183
+ }, [serverResult]);
184
184
 
185
185
  // Instead of using state, which would be local to just this hook instance,
186
186
  // we use a shared in-memory cache.
@@ -8,6 +8,29 @@ import {useRequestInterception} from "./use-request-interception.js";
8
8
 
9
9
  import type {Result, ValidCacheData} from "../util/types.js";
10
10
 
11
+ type ServerEffectOptions = {|
12
+ /**
13
+ * When `true`, the result of the effect when fulfilled using Wonder Blocks
14
+ * Data will be stored in the hydration cache for hydrating client-side;
15
+ * otherwise, the result will be stored in the server-side-only cache.
16
+ *
17
+ * This should only be set to `false` if something else will be responsible
18
+ * for hydration of the data on the client-side (for example, if Apollo's
19
+ * hydration support is used).
20
+ *
21
+ * Default is `true`.
22
+ */
23
+ hydrate?: boolean,
24
+
25
+ /**
26
+ * When `true`, the effect will not be tracked for fulfillment; otherwise,
27
+ * the effect will be tracked for fulfillment.
28
+ *
29
+ * Default is `false`.
30
+ */
31
+ skip?: boolean,
32
+ |};
33
+
11
34
  /**
12
35
  * Hook to perform an asynchronous action during server-side rendering.
13
36
  *
@@ -26,8 +49,10 @@ import type {Result, ValidCacheData} from "../util/types.js";
26
49
  export const useServerEffect = <TData: ValidCacheData>(
27
50
  requestId: string,
28
51
  handler: () => Promise<TData>,
29
- hydrate: boolean = true,
52
+ options: ServerEffectOptions = ({}: $Shape<ServerEffectOptions>),
30
53
  ): ?Result<TData> => {
54
+ const {hydrate = true, skip = false} = options;
55
+
31
56
  // Plug in to the request interception framework for code that wants
32
57
  // to use that.
33
58
  const interceptedHandler = useRequestInterception(requestId, handler);
@@ -39,11 +64,11 @@ export const useServerEffect = <TData: ValidCacheData>(
39
64
  // initial render - and subsequent renders on the client it will be null.
40
65
  const cachedResult = SsrCache.Default.getEntry<TData>(requestId);
41
66
 
42
- // We only track data requests when we are server-side and we don't
43
- // already have a result, as given by the cachedData (which is also the
44
- // initial value for the result state).
67
+ // We only track data requests when we are server-side, we are not skipping
68
+ // the request, and we don't already have a result, as given by the
69
+ // cachedData (which is also the initial value for the result state).
45
70
  const maybeTrack = useContext(TrackerContext);
46
- if (cachedResult == null && Server.isServerSide()) {
71
+ if (!skip && cachedResult == null && Server.isServerSide()) {
47
72
  maybeTrack?.(requestId, interceptedHandler, hydrate);
48
73
  }
49
74
 
@@ -1,15 +0,0 @@
1
- // @flow
2
-
3
- /**
4
- * Simple implementation to represent aborting.
5
- *
6
- * Other frameworks may provide this too, so we won't be sharing this with
7
- * the outside world. It's just a utility for test and internal use whenever
8
- * we need to represent the concept of aborted things.
9
- */
10
- export class AbortError extends Error {
11
- constructor(message: string) {
12
- super(message);
13
- this.name = "AbortError";
14
- }
15
- }