@khanacademy/wonder-blocks-data 6.0.1 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @khanacademy/wonder-blocks-data
2
2
 
3
+ ## 7.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 34407c4a: `useServerEffect` now has a `skip` option, preventing the request from getting tracked for server-side fulfillment
8
+
3
9
  ## 6.0.1
4
10
 
5
11
  ### Patch Changes
package/dist/es/index.js CHANGED
@@ -600,21 +600,6 @@ class TrackData extends React.Component {
600
600
 
601
601
  }
602
602
 
603
- /**
604
- * Simple implementation to represent aborting.
605
- *
606
- * Other frameworks may provide this too, so we won't be sharing this with
607
- * the outside world. It's just a utility for test and internal use whenever
608
- * we need to represent the concept of aborted things.
609
- */
610
- class AbortError extends Error {
611
- constructor(message) {
612
- super(message);
613
- this.name = "AbortError";
614
- }
615
-
616
- }
617
-
618
603
  const loadingStatus = Object.freeze({
619
604
  status: "loading"
620
605
  });
@@ -728,22 +713,26 @@ const useRequestInterception = (requestId, handler) => {
728
713
  *
729
714
  * The asynchronous action is never invoked on the client-side.
730
715
  */
731
- const useServerEffect = (requestId, handler, hydrate = true) => {
732
- // Plug in to the request interception framework for code that wants
716
+ const useServerEffect = (requestId, handler, options = {}) => {
717
+ const {
718
+ hydrate = true,
719
+ skip = false
720
+ } = options; // Plug in to the request interception framework for code that wants
733
721
  // to use that.
722
+
734
723
  const interceptedHandler = useRequestInterception(requestId, handler); // If we're server-side or hydrating, we'll have a cached entry to use.
735
724
  // So we get that and use it to initialize our state.
736
725
  // This works in both hydration and SSR because the very first call to
737
726
  // this will have cached data in those cases as it will be present on the
738
727
  // initial render - and subsequent renders on the client it will be null.
739
728
 
740
- const cachedResult = SsrCache.Default.getEntry(requestId); // We only track data requests when we are server-side and we don't
741
- // already have a result, as given by the cachedData (which is also the
742
- // initial value for the result state).
729
+ const cachedResult = SsrCache.Default.getEntry(requestId); // We only track data requests when we are server-side, we are not skipping
730
+ // the request, and we don't already have a result, as given by the
731
+ // cachedData (which is also the initial value for the result state).
743
732
 
744
733
  const maybeTrack = useContext(TrackerContext);
745
734
 
746
- if (cachedResult == null && Server.isServerSide()) {
735
+ if (!skip && cachedResult == null && Server.isServerSide()) {
747
736
  maybeTrack == null ? void 0 : maybeTrack(requestId, interceptedHandler, hydrate);
748
737
  } // A null result means there was no result to hydrate.
749
738
 
@@ -992,10 +981,11 @@ const useHydratableEffect = (requestId, handler, options = {}) => {
992
981
  // When client-side, this will look up any response for hydration; it does
993
982
  // not invoke the handler.
994
983
 
995
- const serverResult = useServerEffect(requestId, // If we're skipped (unlikely in server worlds, but maybe),
996
- // just give an aborted response.
997
- skip ? () => Promise.reject(new AbortError("skipped")) : handler, // Only hydrate if our behavior isn't telling us not to.
998
- clientBehavior !== WhenClientSide.DoNotHydrate);
984
+ const serverResult = useServerEffect(requestId, handler, {
985
+ // Only hydrate if our behavior isn't telling us not to.
986
+ hydrate: clientBehavior !== WhenClientSide.DoNotHydrate,
987
+ skip
988
+ });
999
989
  const getDefaultCacheValue = React.useCallback(() => {
1000
990
  // If we don't have a requestId, it's our first render, the one
1001
991
  // where we hydrated. So defer to our clientBehavior value.
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.
@@ -965,11 +964,11 @@ const useHydratableEffect = (requestId, handler, options = {}) => {
965
964
  }, [serverResult]); // Instead of using state, which would be local to just this hook instance,
966
965
  // we use a shared in-memory cache.
967
966
 
968
- 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
969
968
  scope, // The scope of the cached items
970
969
  getDefaultCacheValue); // When we're client-side, we ultimately want the result from this call.
971
970
 
972
- 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, {
973
972
  skip,
974
973
  onResultChanged,
975
974
  retainResultOnChange,
@@ -1061,7 +1060,7 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
1061
1060
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
1062
1061
  /* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7);
1063
1062
  /* harmony import */ var _util_ssr_cache_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
1064
- /* 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);
1065
1064
  /* harmony import */ var _use_request_interception_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(16);
1066
1065
 
1067
1066
 
@@ -1085,22 +1084,26 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
1085
1084
  *
1086
1085
  * The asynchronous action is never invoked on the client-side.
1087
1086
  */
1088
- const useServerEffect = (requestId, handler, hydrate = true) => {
1089
- // 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
1090
1092
  // to use that.
1093
+
1091
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.
1092
1095
  // So we get that and use it to initialize our state.
1093
1096
  // This works in both hydration and SSR because the very first call to
1094
1097
  // this will have cached data in those cases as it will be present on the
1095
1098
  // initial render - and subsequent renders on the client it will be null.
1096
1099
 
1097
- 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
1098
- // already have a result, as given by the cachedData (which is also the
1099
- // 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).
1100
1103
 
1101
1104
  const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__[/* TrackerContext */ "b"]);
1102
1105
 
1103
- 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()) {
1104
1107
  maybeTrack == null ? void 0 : maybeTrack(requestId, interceptedHandler, hydrate);
1105
1108
  } // A null result means there was no result to hydrate.
1106
1109
 
@@ -1517,8 +1520,8 @@ const GqlRouter = ({
1517
1520
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1518
1521
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1519
1522
  /* harmony import */ var _util_merge_gql_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(18);
1520
- /* harmony import */ var _use_gql_router_context_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(26);
1521
- /* 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);
1522
1525
 
1523
1526
 
1524
1527
 
@@ -1562,27 +1565,6 @@ const useGql = (context = {}) => {
1562
1565
  /* 24 */
1563
1566
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1564
1567
 
1565
- "use strict";
1566
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return AbortError; });
1567
- /**
1568
- * Simple implementation to represent aborting.
1569
- *
1570
- * Other frameworks may provide this too, so we won't be sharing this with
1571
- * the outside world. It's just a utility for test and internal use whenever
1572
- * we need to represent the concept of aborted things.
1573
- */
1574
- class AbortError extends Error {
1575
- constructor(message) {
1576
- super(message);
1577
- this.name = "AbortError";
1578
- }
1579
-
1580
- }
1581
-
1582
- /***/ }),
1583
- /* 25 */
1584
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
1585
-
1586
1568
  "use strict";
1587
1569
  /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return resultFromCachedResponse; });
1588
1570
  /* harmony import */ var _status_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4);
@@ -1620,7 +1602,7 @@ const resultFromCachedResponse = cacheEntry => {
1620
1602
  };
1621
1603
 
1622
1604
  /***/ }),
1623
- /* 26 */
1605
+ /* 25 */
1624
1606
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1625
1607
 
1626
1608
  "use strict";
@@ -1672,7 +1654,7 @@ const useGqlRouterContext = (contextOverrides = {}) => {
1672
1654
  };
1673
1655
 
1674
1656
  /***/ }),
1675
- /* 27 */
1657
+ /* 26 */
1676
1658
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1677
1659
 
1678
1660
  "use strict";
@@ -1742,7 +1724,7 @@ const getGqlDataFromResponse = async response => {
1742
1724
  };
1743
1725
 
1744
1726
  /***/ }),
1745
- /* 28 */
1727
+ /* 27 */
1746
1728
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
1747
1729
 
1748
1730
  "use strict";
@@ -1889,7 +1871,7 @@ const removeAllFromCache = predicate => _util_ssr_cache_js__WEBPACK_IMPORTED_MOD
1889
1871
 
1890
1872
 
1891
1873
  /***/ }),
1892
- /* 29 */
1874
+ /* 28 */
1893
1875
  /***/ (function(module, exports, __webpack_require__) {
1894
1876
 
1895
1877
  "use strict";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-data",
3
- "version": "6.0.1",
3
+ "version": "7.0.0",
4
4
  "design": "v1",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -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
  );
@@ -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
@@ -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
- }