@khanacademy/wonder-blocks-data 2.3.2 → 3.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/CHANGELOG.md +14 -0
- package/dist/es/index.js +210 -439
- package/dist/index.js +235 -478
- package/docs.md +19 -13
- package/package.json +6 -7
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +40 -160
- package/src/__tests__/generated-snapshot.test.js +15 -195
- package/src/components/__tests__/data.test.js +159 -965
- package/src/components/__tests__/intercept-data.test.js +9 -66
- package/src/components/__tests__/track-data.test.js +6 -5
- package/src/components/data.js +9 -119
- package/src/components/data.md +38 -60
- package/src/components/intercept-context.js +2 -3
- package/src/components/intercept-data.js +2 -34
- package/src/components/intercept-data.md +7 -105
- package/src/hooks/__tests__/use-data.test.js +826 -0
- package/src/hooks/use-data.js +143 -0
- package/src/index.js +1 -3
- package/src/util/__tests__/memory-cache.test.js +134 -35
- package/src/util/__tests__/request-fulfillment.test.js +21 -36
- package/src/util/__tests__/request-handler.test.js +30 -30
- package/src/util/__tests__/request-tracking.test.js +29 -30
- package/src/util/__tests__/response-cache.test.js +521 -561
- package/src/util/__tests__/result-from-cache-entry.test.js +68 -0
- package/src/util/memory-cache.js +20 -15
- package/src/util/request-fulfillment.js +4 -0
- package/src/util/request-handler.js +4 -28
- package/src/util/request-handler.md +0 -32
- package/src/util/request-tracking.js +2 -3
- package/src/util/response-cache.js +50 -110
- package/src/util/result-from-cache-entry.js +38 -0
- package/src/util/types.js +14 -35
- package/LICENSE +0 -21
- package/src/components/__tests__/intercept-cache.test.js +0 -124
- package/src/components/__tests__/internal-data.test.js +0 -1030
- package/src/components/intercept-cache.js +0 -79
- package/src/components/intercept-cache.md +0 -103
- package/src/components/internal-data.js +0 -219
- package/src/util/__tests__/no-cache.test.js +0 -112
- package/src/util/no-cache.js +0 -66
- package/src/util/no-cache.md +0 -66
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 =
|
|
85
|
+
/******/ return __webpack_require__(__webpack_require__.s = 13);
|
|
86
86
|
/******/ })
|
|
87
87
|
/************************************************************************/
|
|
88
88
|
/******/ ([
|
|
@@ -93,15 +93,19 @@ module.exports = require("react");
|
|
|
93
93
|
|
|
94
94
|
/***/ }),
|
|
95
95
|
/* 1 */
|
|
96
|
+
/***/ (function(module, exports) {
|
|
97
|
+
|
|
98
|
+
module.exports = require("@khanacademy/wonder-blocks-core");
|
|
99
|
+
|
|
100
|
+
/***/ }),
|
|
101
|
+
/* 2 */
|
|
96
102
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
97
103
|
|
|
98
104
|
"use strict";
|
|
99
105
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return ResponseCache; });
|
|
100
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
106
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
101
107
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
102
108
|
/* harmony import */ var _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
|
|
103
|
-
/* harmony import */ var _no_cache_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
|
|
104
|
-
|
|
105
109
|
|
|
106
110
|
|
|
107
111
|
|
|
@@ -126,24 +130,22 @@ class ResponseCache {
|
|
|
126
130
|
return _default;
|
|
127
131
|
}
|
|
128
132
|
|
|
129
|
-
constructor(
|
|
133
|
+
constructor(hydrationCache = null, ssrOnlyCache = null) {
|
|
130
134
|
this.initialize = source => {
|
|
131
|
-
if (this.
|
|
135
|
+
if (this._hydrationCache.inUse) {
|
|
132
136
|
throw new Error("Cannot initialize data response cache more than once");
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
try {
|
|
136
|
-
this.
|
|
140
|
+
this._hydrationCache = new _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"](source);
|
|
137
141
|
} catch (e) {
|
|
138
142
|
throw new Error(`An error occurred trying to initialize the data response cache: ${e}`);
|
|
139
143
|
}
|
|
140
144
|
};
|
|
141
145
|
|
|
142
|
-
this.cacheData = (handler, options, data) => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
});
|
|
146
|
-
};
|
|
146
|
+
this.cacheData = (handler, options, data) => this._setCacheEntry(handler, options, {
|
|
147
|
+
data
|
|
148
|
+
});
|
|
147
149
|
|
|
148
150
|
this.cacheError = (handler, options, error) => {
|
|
149
151
|
const errorMessage = typeof error === "string" ? error : error.message;
|
|
@@ -153,110 +155,64 @@ class ResponseCache {
|
|
|
153
155
|
};
|
|
154
156
|
|
|
155
157
|
this.getEntry = (handler, options) => {
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// This allows us to use our hydrated cache during hydration.
|
|
167
|
-
// If we just returned null when the custom cache didn't have it,
|
|
168
|
-
// we would never hydrate properly.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const internalEntry = this._defaultCache(handler).retrieve(handler, options); // If we are not server-side and we hydrated something that the custom
|
|
172
|
-
// cache didn't have, we need to make sure the custom cache contains
|
|
173
|
-
// that value.
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (this._ssrOnlyCache == null && handler.cache != null && internalEntry != null) {
|
|
177
|
-
// Yes, if this throws, we will have a problem. We want that.
|
|
178
|
-
// Bad cache implementations should be overt.
|
|
179
|
-
handler.cache.store(handler, options, internalEntry); // We now delete this from our in-memory cache as we don't need it.
|
|
158
|
+
// Get the cached entry for this value.
|
|
159
|
+
// If the handler wants WB Data to hydrate (i.e. handler.hydrate is
|
|
160
|
+
// true), we use our hydration cache. Otherwise, if we're server-side
|
|
161
|
+
// we use our SSR-only cache. Otherwise, there's no entry to return.
|
|
162
|
+
const cache = handler.hydrate ? this._hydrationCache : _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide() ? this._ssrOnlyCache : undefined;
|
|
163
|
+
const internalEntry = cache == null ? void 0 : cache.retrieve(handler, options); // If we are not server-side and we hydrated something, let's clear
|
|
164
|
+
// that from the hydration cache to save memory.
|
|
165
|
+
|
|
166
|
+
if (this._ssrOnlyCache == null && internalEntry != null) {
|
|
167
|
+
// We now delete this from our hydration cache as we don't need it.
|
|
180
168
|
// This does mean that if another handler of the same type but
|
|
181
|
-
// without
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
|
|
185
|
-
this.
|
|
169
|
+
// without some sort of linked cache won't get the value, but
|
|
170
|
+
// that's not an expected use-case. If two different places use the
|
|
171
|
+
// same handler and options (i.e. the same request), then the
|
|
172
|
+
// handler should cater to that to ensure they share the result.
|
|
173
|
+
this._hydrationCache.remove(handler, options);
|
|
186
174
|
}
|
|
187
175
|
|
|
188
176
|
return internalEntry;
|
|
189
177
|
};
|
|
190
178
|
|
|
191
179
|
this.remove = (handler, options) => {
|
|
180
|
+
var _this$_ssrOnlyCache$r, _this$_ssrOnlyCache;
|
|
181
|
+
|
|
192
182
|
// NOTE(somewhatabstract): We could invoke removeAll with a predicate
|
|
193
183
|
// to match the key of the entry we're removing, but that's an
|
|
194
184
|
// inefficient way to remove a single item, so let's not do that.
|
|
195
|
-
//
|
|
196
|
-
|
|
197
|
-
const customCache = this._ssrOnlyCache == null ? handler.cache : null;
|
|
198
|
-
const removedCustom = !!(customCache != null && customCache.remove(handler, options)); // Delete the entry from our internal cache.
|
|
199
|
-
// Even if we have a custom cache, we want to make sure we still
|
|
200
|
-
// removed the same value from internal cache since this could be
|
|
201
|
-
// getting called before hydration for some complex advanced usage
|
|
202
|
-
// reason.
|
|
203
|
-
|
|
204
|
-
return this._defaultCache(handler).remove(handler, options) || removedCustom;
|
|
185
|
+
// Delete the entry from the appropriate cache.
|
|
186
|
+
return handler.hydrate ? this._hydrationCache.remove(handler, options) : (_this$_ssrOnlyCache$r = (_this$_ssrOnlyCache = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache.remove(handler, options)) != null ? _this$_ssrOnlyCache$r : false;
|
|
205
187
|
};
|
|
206
188
|
|
|
207
189
|
this.removeAll = (handler, predicate) => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// Even if we have a custom cache, we want to make sure we still
|
|
213
|
-
// removed the same value from internal cache since this could be
|
|
214
|
-
// getting called before hydration for some complex advanced usage
|
|
215
|
-
// reason.
|
|
216
|
-
|
|
217
|
-
const removedCount = this._defaultCache(handler).removeAll(handler, predicate); // We have no idea which keys were removed from which caches,
|
|
218
|
-
// so we can't dedupe the remove counts based on keys.
|
|
219
|
-
// That's why we return the total records deleted rather than the
|
|
220
|
-
// total keys deleted.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return removedCount + removedCountCustom;
|
|
190
|
+
var _this$_ssrOnlyCache$r2, _this$_ssrOnlyCache2;
|
|
191
|
+
|
|
192
|
+
// Apply the predicate to what we have in the appropriate cache.
|
|
193
|
+
return handler.hydrate ? this._hydrationCache.removeAll(handler, predicate) : (_this$_ssrOnlyCache$r2 = (_this$_ssrOnlyCache2 = this._ssrOnlyCache) == null ? void 0 : _this$_ssrOnlyCache2.removeAll(handler, predicate)) != null ? _this$_ssrOnlyCache$r2 : 0;
|
|
224
194
|
};
|
|
225
195
|
|
|
226
196
|
this.cloneHydratableData = () => {
|
|
227
197
|
// We return our hydration cache only.
|
|
228
|
-
return this.
|
|
198
|
+
return this._hydrationCache.cloneData();
|
|
229
199
|
};
|
|
230
200
|
|
|
231
201
|
this._ssrOnlyCache = _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide() ? ssrOnlyCache || new _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"]() : undefined;
|
|
232
|
-
this.
|
|
233
|
-
}
|
|
234
|
-
/**
|
|
235
|
-
* Returns the default cache to use for the given handler.
|
|
236
|
-
*/
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
_defaultCache(handler) {
|
|
240
|
-
if (handler.hydrate) {
|
|
241
|
-
return this._hydrationAndDefaultCache;
|
|
242
|
-
} // If the handler doesn't want to hydrate, we return the SSR-only cache.
|
|
243
|
-
// If we are client-side, we return our non-caching implementation.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return this._ssrOnlyCache || _no_cache_js__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"].Default;
|
|
202
|
+
this._hydrationCache = hydrationCache || new _memory_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"]();
|
|
247
203
|
}
|
|
248
204
|
|
|
249
205
|
_setCacheEntry(handler, options, entry) {
|
|
250
206
|
const frozenEntry = Object.freeze(entry);
|
|
251
207
|
|
|
252
|
-
if (this._ssrOnlyCache
|
|
253
|
-
// We are
|
|
254
|
-
//
|
|
255
|
-
handler.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
208
|
+
if (this._ssrOnlyCache != null) {
|
|
209
|
+
// We are server-side.
|
|
210
|
+
// We need to store this value.
|
|
211
|
+
if (handler.hydrate) {
|
|
212
|
+
this._hydrationCache.store(handler, options, frozenEntry);
|
|
213
|
+
} else {
|
|
214
|
+
this._ssrOnlyCache.store(handler, options, frozenEntry);
|
|
215
|
+
}
|
|
260
216
|
}
|
|
261
217
|
|
|
262
218
|
return frozenEntry;
|
|
@@ -270,12 +226,6 @@ class ResponseCache {
|
|
|
270
226
|
|
|
271
227
|
}
|
|
272
228
|
|
|
273
|
-
/***/ }),
|
|
274
|
-
/* 2 */
|
|
275
|
-
/***/ (function(module, exports) {
|
|
276
|
-
|
|
277
|
-
module.exports = require("@khanacademy/wonder-blocks-core");
|
|
278
|
-
|
|
279
229
|
/***/ }),
|
|
280
230
|
/* 3 */
|
|
281
231
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
@@ -285,7 +235,7 @@ module.exports = require("@khanacademy/wonder-blocks-core");
|
|
|
285
235
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestTracker; });
|
|
286
236
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
287
237
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
288
|
-
/* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
238
|
+
/* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
|
|
289
239
|
/* harmony import */ var _request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
|
|
290
240
|
|
|
291
241
|
|
|
@@ -450,42 +400,138 @@ const InterceptContext = /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["create
|
|
|
450
400
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
451
401
|
|
|
452
402
|
"use strict";
|
|
453
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
* handler as this ensures that cache space and memory are never used for the
|
|
464
|
-
* requested data after hydration has finished.
|
|
465
|
-
*/
|
|
403
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return useData; });
|
|
404
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
405
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
406
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(0);
|
|
407
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
|
|
408
|
+
/* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
|
|
409
|
+
/* harmony import */ var _components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
|
|
410
|
+
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3);
|
|
411
|
+
/* harmony import */ var _util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(12);
|
|
412
|
+
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(2);
|
|
466
413
|
|
|
467
|
-
class NoCache {
|
|
468
|
-
constructor() {
|
|
469
|
-
this.store = (handler, options, entry) => {
|
|
470
|
-
/* empty */
|
|
471
|
-
};
|
|
472
414
|
|
|
473
|
-
this.retrieve = (handler, options) => null;
|
|
474
415
|
|
|
475
|
-
this.remove = (handler, options) => false;
|
|
476
416
|
|
|
477
|
-
this.removeAll = (handler, predicate) => 0;
|
|
478
|
-
}
|
|
479
417
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
const useData = (handler, options) => {
|
|
421
|
+
// If we're server-side or hydrating, we'll have a cached entry to use.
|
|
422
|
+
// So we get that and use it to initialize our state.
|
|
423
|
+
// This works in both hydration and SSR because the very first call to
|
|
424
|
+
// this will have cached data in those cases as it will be present on the
|
|
425
|
+
// initial render - and subsequent renders on the client it will be null.
|
|
426
|
+
const cachedResult = _util_response_cache_js__WEBPACK_IMPORTED_MODULE_6__[/* ResponseCache */ "a"].Default.getEntry(handler, options);
|
|
427
|
+
const [result, setResult] = Object(react__WEBPACK_IMPORTED_MODULE_1__["useState"])(cachedResult); // Lookup to see if there's an interceptor for the handler.
|
|
428
|
+
// If we have one, we need to replace the handler with one that
|
|
429
|
+
// uses the interceptor.
|
|
430
|
+
|
|
431
|
+
const interceptorMap = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_components_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"]);
|
|
432
|
+
const interceptor = interceptorMap[handler.type]; // If we have an interceptor, we need to replace the handler with one that
|
|
433
|
+
// uses the interceptor. This helper function generates a new handler.
|
|
434
|
+
// We need this before we track the request as we want the interceptor
|
|
435
|
+
// to also work for tracked requests to simplify testing the server-side
|
|
436
|
+
// request fulfillment.
|
|
437
|
+
|
|
438
|
+
const getMaybeInterceptedHandler = () => {
|
|
439
|
+
if (interceptor == null) {
|
|
440
|
+
return handler;
|
|
483
441
|
}
|
|
484
442
|
|
|
485
|
-
|
|
486
|
-
|
|
443
|
+
const fulfillRequestFn = options => {
|
|
444
|
+
var _interceptor$fulfillR;
|
|
487
445
|
|
|
488
|
-
|
|
446
|
+
return (_interceptor$fulfillR = interceptor.fulfillRequest(options)) != null ? _interceptor$fulfillR : handler.fulfillRequest(options);
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
return {
|
|
450
|
+
fulfillRequest: fulfillRequestFn,
|
|
451
|
+
getKey: options => handler.getKey(options),
|
|
452
|
+
type: handler.type,
|
|
453
|
+
hydrate: handler.hydrate
|
|
454
|
+
};
|
|
455
|
+
}; // We only track data requests when we are server-side and we don't
|
|
456
|
+
// already have a result, as given by the cachedData (which is also the
|
|
457
|
+
// initial value for the result state).
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
const maybeTrack = Object(react__WEBPACK_IMPORTED_MODULE_1__["useContext"])(_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_4__[/* TrackerContext */ "b"]);
|
|
461
|
+
|
|
462
|
+
if (result == null && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
463
|
+
maybeTrack == null ? void 0 : maybeTrack(getMaybeInterceptedHandler(), options);
|
|
464
|
+
} // We need to update our request when the handler changes or the key
|
|
465
|
+
// to the options change, so we keep track of those.
|
|
466
|
+
// However, even if we are hydrating from cache, we still need to make the
|
|
467
|
+
// request at least once, so we do not initialize these references.
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
const handlerRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])();
|
|
471
|
+
const keyRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])();
|
|
472
|
+
const interceptorRef = Object(react__WEBPACK_IMPORTED_MODULE_1__["useRef"])(); // This effect will ensure that we fulfill the request as desired.
|
|
473
|
+
|
|
474
|
+
Object(react__WEBPACK_IMPORTED_MODULE_1__["useEffect"])(() => {
|
|
475
|
+
// If we are server-side, then just skip the effect. We track requests
|
|
476
|
+
// during SSR and fulfill them outside of the React render cycle.
|
|
477
|
+
// NOTE: This shouldn't happen since effects would not run on the server
|
|
478
|
+
// but let's be defensive - I think it makes the code clearer.
|
|
479
|
+
|
|
480
|
+
/* istanbul ignore next */
|
|
481
|
+
if (_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__["Server"].isServerSide()) {
|
|
482
|
+
return;
|
|
483
|
+
} // Update our refs to the current handler and key.
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
handlerRef.current = handler;
|
|
487
|
+
keyRef.current = handler.getKey(options);
|
|
488
|
+
interceptorRef.current = interceptor; // If we're not hydrating a result, we want to make sure we set our
|
|
489
|
+
// result to null so that we're in the loading state.
|
|
490
|
+
|
|
491
|
+
if (cachedResult == null) {
|
|
492
|
+
// Mark ourselves as loading.
|
|
493
|
+
setResult(null);
|
|
494
|
+
} // We aren't server-side, so let's make the request.
|
|
495
|
+
// The request handler is in control of whether that request actually
|
|
496
|
+
// happens or not.
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
let cancel = false;
|
|
500
|
+
_util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"].Default.fulfill(getMaybeInterceptedHandler(), options).then(updateEntry => {
|
|
501
|
+
if (cancel) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
setResult(updateEntry);
|
|
506
|
+
return;
|
|
507
|
+
}).catch(e => {
|
|
508
|
+
if (cancel) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* We should never get here as errors in fulfillment are part
|
|
513
|
+
* of the `then`, but if we do.
|
|
514
|
+
*/
|
|
515
|
+
// eslint-disable-next-line no-console
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
console.error(`Unexpected error occurred during data fulfillment: ${e}`);
|
|
519
|
+
setResult({
|
|
520
|
+
data: null,
|
|
521
|
+
error: typeof e === "string" ? e : e.message
|
|
522
|
+
});
|
|
523
|
+
return;
|
|
524
|
+
});
|
|
525
|
+
return () => {
|
|
526
|
+
cancel = true;
|
|
527
|
+
}; // - handler.getKey is a proxy for options
|
|
528
|
+
// - We don't want to trigger on cachedResult changing, we're
|
|
529
|
+
// just using that as a flag for render state if the other things
|
|
530
|
+
// trigger this effect.
|
|
531
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
532
|
+
}, [handler, handler.getKey(options), interceptor]);
|
|
533
|
+
return Object(_util_result_from_cache_entry_js__WEBPACK_IMPORTED_MODULE_5__[/* resultFromCacheEntry */ "a"])(result);
|
|
534
|
+
};
|
|
489
535
|
|
|
490
536
|
/***/ }),
|
|
491
537
|
/* 6 */
|
|
@@ -493,7 +539,7 @@ class NoCache {
|
|
|
493
539
|
|
|
494
540
|
"use strict";
|
|
495
541
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return RequestFulfillment; });
|
|
496
|
-
/* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
542
|
+
/* harmony import */ var _response_cache_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
497
543
|
|
|
498
544
|
|
|
499
545
|
let _default;
|
|
@@ -546,6 +592,8 @@ class RequestFulfillment {
|
|
|
546
592
|
delete handlerRequests[key];
|
|
547
593
|
/**
|
|
548
594
|
* Let's cache the data!
|
|
595
|
+
*
|
|
596
|
+
* NOTE: This only caches when we're server side.
|
|
549
597
|
*/
|
|
550
598
|
|
|
551
599
|
return cacheData(handler, options, data);
|
|
@@ -553,6 +601,8 @@ class RequestFulfillment {
|
|
|
553
601
|
delete handlerRequests[key];
|
|
554
602
|
/**
|
|
555
603
|
* Let's cache the error!
|
|
604
|
+
*
|
|
605
|
+
* NOTE: This only caches when we're server side.
|
|
556
606
|
*/
|
|
557
607
|
|
|
558
608
|
return cacheError(handler, options, error);
|
|
@@ -596,9 +646,7 @@ function deepClone(source) {
|
|
|
596
646
|
*
|
|
597
647
|
* Special case cache implementation for the memory cache.
|
|
598
648
|
*
|
|
599
|
-
* This is only used within our framework
|
|
600
|
-
* provide this as a custom cache as the framework will default to this in the
|
|
601
|
-
* absence of a custom cache. We use this for SSR too (see ./response-cache.js).
|
|
649
|
+
* This is only used within our framework for SSR (see ./response-cache.js).
|
|
602
650
|
*/
|
|
603
651
|
|
|
604
652
|
|
|
@@ -606,7 +654,7 @@ class MemoryCache {
|
|
|
606
654
|
constructor(source = null) {
|
|
607
655
|
this.store = (handler, options, entry) => {
|
|
608
656
|
const requestType = handler.type;
|
|
609
|
-
const frozenEntry = Object.
|
|
657
|
+
const frozenEntry = Object.freeze(entry); // Ensure we have a cache location for this handler type.
|
|
610
658
|
|
|
611
659
|
this._cache[requestType] = this._cache[requestType] || {}; // Cache the data.
|
|
612
660
|
|
|
@@ -666,16 +714,22 @@ class MemoryCache {
|
|
|
666
714
|
|
|
667
715
|
if (!handlerCache) {
|
|
668
716
|
return 0;
|
|
669
|
-
}
|
|
670
|
-
|
|
717
|
+
}
|
|
671
718
|
|
|
672
719
|
let removedCount = 0;
|
|
673
720
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
721
|
+
if (typeof predicate === "function") {
|
|
722
|
+
// Apply the predicate to what we have cached.
|
|
723
|
+
for (const [key, entry] of Object.entries(handlerCache)) {
|
|
724
|
+
if (predicate(key, entry)) {
|
|
725
|
+
removedCount++;
|
|
726
|
+
delete handlerCache[key];
|
|
727
|
+
}
|
|
678
728
|
}
|
|
729
|
+
} else {
|
|
730
|
+
// We're removing everything so delete the entire subcache.
|
|
731
|
+
removedCount = Object.keys(handlerCache).length;
|
|
732
|
+
delete this._cache[requestType];
|
|
679
733
|
}
|
|
680
734
|
|
|
681
735
|
return removedCount;
|
|
@@ -705,6 +759,12 @@ class MemoryCache {
|
|
|
705
759
|
}
|
|
706
760
|
}
|
|
707
761
|
}
|
|
762
|
+
/**
|
|
763
|
+
* Indicate if this cache is being used or now.
|
|
764
|
+
*
|
|
765
|
+
* When the cache has entries, returns `true`; otherwise, returns `false`.
|
|
766
|
+
*/
|
|
767
|
+
|
|
708
768
|
|
|
709
769
|
get inUse() {
|
|
710
770
|
return Object.keys(this._cache).length > 0;
|
|
@@ -725,9 +785,8 @@ class MemoryCache {
|
|
|
725
785
|
* use with the Wonder Blocks Data framework.
|
|
726
786
|
*/
|
|
727
787
|
class RequestHandler {
|
|
728
|
-
constructor(type,
|
|
788
|
+
constructor(type, hydrate = true) {
|
|
729
789
|
this._type = type;
|
|
730
|
-
this._cache = cache || null;
|
|
731
790
|
this._hydrate = !!hydrate;
|
|
732
791
|
}
|
|
733
792
|
|
|
@@ -735,26 +794,10 @@ class RequestHandler {
|
|
|
735
794
|
return this._type;
|
|
736
795
|
}
|
|
737
796
|
|
|
738
|
-
get cache() {
|
|
739
|
-
return this._cache;
|
|
740
|
-
}
|
|
741
|
-
|
|
742
797
|
get hydrate() {
|
|
743
798
|
return this._hydrate;
|
|
744
799
|
}
|
|
745
800
|
|
|
746
|
-
shouldRefreshCache(options, cachedEntry) {
|
|
747
|
-
/**
|
|
748
|
-
* By default, the cache needs a refresh if the current entry is an
|
|
749
|
-
* error.
|
|
750
|
-
*
|
|
751
|
-
* This means that an error will cause a re-request on render.
|
|
752
|
-
* Useful if the server rendered an error, as it means the client
|
|
753
|
-
* will update after rehydration.
|
|
754
|
-
*/
|
|
755
|
-
return cachedEntry == null || cachedEntry.error != null;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
801
|
getKey(options) {
|
|
759
802
|
try {
|
|
760
803
|
return options === undefined ? "undefined" : JSON.stringify(options);
|
|
@@ -777,7 +820,7 @@ class RequestHandler {
|
|
|
777
820
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return TrackData; });
|
|
778
821
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
779
822
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
780
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
823
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
|
|
781
824
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
782
825
|
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
783
826
|
|
|
@@ -805,14 +848,9 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
805
848
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
806
849
|
|
|
807
850
|
"use strict";
|
|
808
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return Data; });
|
|
809
851
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
810
852
|
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
811
|
-
/* harmony import */ var
|
|
812
|
-
/* harmony import */ var _internal_data_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13);
|
|
813
|
-
/* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4);
|
|
814
|
-
|
|
815
|
-
|
|
853
|
+
/* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
816
854
|
|
|
817
855
|
|
|
818
856
|
|
|
@@ -821,83 +859,12 @@ class TrackData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
821
859
|
* requirements can be placed in a React application in a manner that will
|
|
822
860
|
* support server-side rendering and efficient caching.
|
|
823
861
|
*/
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
} = this.props;
|
|
829
|
-
|
|
830
|
-
if (!interceptor) {
|
|
831
|
-
return handler;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
const {
|
|
835
|
-
fulfillRequest,
|
|
836
|
-
shouldRefreshCache
|
|
837
|
-
} = interceptor;
|
|
838
|
-
const fulfillRequestFn = fulfillRequest ? options => {
|
|
839
|
-
const interceptedResult = fulfillRequest(options);
|
|
840
|
-
return interceptedResult != null ? interceptedResult : handler.fulfillRequest(options);
|
|
841
|
-
} : options => handler.fulfillRequest(options);
|
|
842
|
-
const shouldRefreshCacheFn = shouldRefreshCache ? (options, cacheEntry) => {
|
|
843
|
-
const interceptedResult = shouldRefreshCache(options, cacheEntry);
|
|
844
|
-
return interceptedResult != null ? interceptedResult : handler.shouldRefreshCache(options, cacheEntry);
|
|
845
|
-
} : (options, cacheEntry) => handler.shouldRefreshCache(options, cacheEntry);
|
|
846
|
-
return {
|
|
847
|
-
fulfillRequest: fulfillRequestFn,
|
|
848
|
-
shouldRefreshCache: shouldRefreshCacheFn,
|
|
849
|
-
getKey: options => handler.getKey(options),
|
|
850
|
-
type: handler.type,
|
|
851
|
-
cache: handler.cache,
|
|
852
|
-
hydrate: handler.hydrate
|
|
853
|
-
};
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
_getCacheLookupFnFromInterceptor(interceptor) {
|
|
857
|
-
const getEntry = interceptor && interceptor.getEntry;
|
|
858
|
-
|
|
859
|
-
if (!getEntry) {
|
|
860
|
-
return _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.getEntry;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
return (handler, options) => {
|
|
864
|
-
// 1. Lookup the current cache value.
|
|
865
|
-
const cacheEntry = _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__[/* ResponseCache */ "a"].Default.getEntry(handler, options); // 2. See if our interceptor wants to override it.
|
|
866
|
-
|
|
867
|
-
const interceptedData = getEntry(options, cacheEntry); // 3. Return the appropriate response.
|
|
868
|
-
|
|
869
|
-
return interceptedData != null ? interceptedData : cacheEntry;
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
render() {
|
|
874
|
-
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_intercept_context_js__WEBPACK_IMPORTED_MODULE_3__[/* default */ "a"].Consumer, null, value => {
|
|
875
|
-
const handlerType = this.props.handler.type;
|
|
876
|
-
const interceptor = value[handlerType];
|
|
877
|
-
|
|
878
|
-
const handler = this._getHandlerFromInterceptor(interceptor);
|
|
879
|
-
|
|
880
|
-
const getEntry = this._getCacheLookupFnFromInterceptor(interceptor);
|
|
881
|
-
/**
|
|
882
|
-
* Need to share our types with InternalData so Flow
|
|
883
|
-
* doesn't need to infer them and find mismatches.
|
|
884
|
-
* However, just deriving a new component creates issues
|
|
885
|
-
* where InternalData starts rerendering too often.
|
|
886
|
-
* Couldn't track down why, so suppressing the error
|
|
887
|
-
* instead.
|
|
888
|
-
*/
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_internal_data_js__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"] // $FlowIgnore[incompatible-type-arg]
|
|
892
|
-
, {
|
|
893
|
-
handler: handler,
|
|
894
|
-
options: this.props.options,
|
|
895
|
-
getEntry: getEntry
|
|
896
|
-
}, result => this.props.children(result));
|
|
897
|
-
});
|
|
898
|
-
}
|
|
862
|
+
const Data = props => {
|
|
863
|
+
const data = Object(_hooks_use_data_js__WEBPACK_IMPORTED_MODULE_1__[/* useData */ "a"])(props.handler, props.options);
|
|
864
|
+
return props.children(data);
|
|
865
|
+
};
|
|
899
866
|
|
|
900
|
-
|
|
867
|
+
/* harmony default export */ __webpack_exports__["a"] = (Data);
|
|
901
868
|
|
|
902
869
|
/***/ }),
|
|
903
870
|
/* 11 */
|
|
@@ -916,9 +883,6 @@ class Data extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
916
883
|
* type of a given handler and provide alternative results. This is mostly
|
|
917
884
|
* useful for testing.
|
|
918
885
|
*
|
|
919
|
-
* Results from this interceptor will end up in the cache. If you
|
|
920
|
-
* wish to only override the cache, use `InterceptCache` instead.
|
|
921
|
-
*
|
|
922
886
|
* This component is not recommended for use in production code as it
|
|
923
887
|
* can prevent predictable functioning of the Wonder Blocks Data framework.
|
|
924
888
|
* One possible side-effect is that inflight requests from the interceptor could
|
|
@@ -935,8 +899,7 @@ class InterceptData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
935
899
|
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"].Consumer, null, value => {
|
|
936
900
|
const handlerType = this.props.handler.type;
|
|
937
901
|
const interceptor = { ...value[handlerType],
|
|
938
|
-
fulfillRequest: this.props.fulfillRequest
|
|
939
|
-
shouldRefreshCache: this.props.shouldRefreshCache || null
|
|
902
|
+
fulfillRequest: this.props.fulfillRequest
|
|
940
903
|
};
|
|
941
904
|
const newValue = { ...value,
|
|
942
905
|
[handlerType]: interceptor
|
|
@@ -954,248 +917,46 @@ class InterceptData extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
|
954
917
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
955
918
|
|
|
956
919
|
"use strict";
|
|
957
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return
|
|
958
|
-
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
959
|
-
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
960
|
-
/* harmony import */ var _intercept_context_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
/**
|
|
965
|
-
* This component provides a mechanism to intercept cache lookups for the
|
|
966
|
-
* type of a given handler and provide alternative values. This is mostly
|
|
967
|
-
* useful for testing.
|
|
968
|
-
*
|
|
969
|
-
* This does not modify the cache in any way. If you want to intercept
|
|
970
|
-
* requests and cache based on the intercept, then use `InterceptData`.
|
|
971
|
-
*
|
|
972
|
-
* This component is generally not suitable for use in production code as it
|
|
973
|
-
* can prevent predictable functioning of the Wonder Blocks Data framework.
|
|
974
|
-
*
|
|
975
|
-
* These components do not chain. If a different `InterceptCache` instance is
|
|
976
|
-
* rendered within this one that intercepts the same handler type, then that
|
|
977
|
-
* new instance will replace this interceptor for its children.
|
|
978
|
-
*/
|
|
979
|
-
class InterceptCache extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
|
|
980
|
-
render() {
|
|
981
|
-
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"].Consumer, null, value => {
|
|
982
|
-
const handlerType = this.props.handler.type;
|
|
983
|
-
const interceptor = { ...value[handlerType],
|
|
984
|
-
getEntry: this.props.getEntry
|
|
985
|
-
};
|
|
986
|
-
const newValue = { ...value,
|
|
987
|
-
[handlerType]: interceptor
|
|
988
|
-
};
|
|
989
|
-
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_intercept_context_js__WEBPACK_IMPORTED_MODULE_1__[/* default */ "a"].Provider, {
|
|
990
|
-
value: newValue
|
|
991
|
-
}, this.props.children);
|
|
992
|
-
});
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
/***/ }),
|
|
998
|
-
/* 13 */
|
|
999
|
-
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1000
|
-
|
|
1001
|
-
"use strict";
|
|
1002
|
-
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return InternalData; });
|
|
1003
|
-
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
|
|
1004
|
-
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
|
|
1005
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
|
|
1006
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__);
|
|
1007
|
-
/* harmony import */ var _util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
|
|
1008
|
-
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3);
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
920
|
+
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return resultFromCacheEntry; });
|
|
1014
921
|
/**
|
|
1015
|
-
*
|
|
1016
|
-
* It is wrapped by Data in order to support intercepts and be exported for use.
|
|
1017
|
-
*
|
|
1018
|
-
* INTERNAL USE ONLY
|
|
922
|
+
* Turns a cache entry into a stateful result.
|
|
1019
923
|
*/
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
this.state = this._buildStateAndfulfillNeeds(props);
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
componentDidMount() {
|
|
1027
|
-
this._mounted = true;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
shouldComponentUpdate(nextProps, nextState) {
|
|
1031
|
-
/**
|
|
1032
|
-
* We only bother updating if our state changed.
|
|
1033
|
-
*
|
|
1034
|
-
* And we only update the state if props changed
|
|
1035
|
-
* or we got new data/error.
|
|
1036
|
-
*/
|
|
1037
|
-
if (!this._propsMatch(nextProps)) {
|
|
1038
|
-
const newState = this._buildStateAndfulfillNeeds(nextProps);
|
|
1039
|
-
|
|
1040
|
-
this.setState(newState);
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
return this.state.loading !== nextState.loading || this.state.data !== nextState.data || this.state.error !== nextState.error;
|
|
1044
|
-
}
|
|
1045
|
-
|
|
1046
|
-
componentWillUnmount() {
|
|
1047
|
-
this._mounted = false;
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
_propsMatch(otherProps) {
|
|
1051
|
-
const {
|
|
1052
|
-
handler,
|
|
1053
|
-
options
|
|
1054
|
-
} = this.props;
|
|
1055
|
-
const {
|
|
1056
|
-
handler: prevHandler,
|
|
1057
|
-
options: prevOptions
|
|
1058
|
-
} = otherProps;
|
|
1059
|
-
return handler === prevHandler && handler.getKey(options) === prevHandler.getKey(prevOptions);
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
_buildStateAndfulfillNeeds(propsAtFulfillment) {
|
|
1063
|
-
const {
|
|
1064
|
-
getEntry,
|
|
1065
|
-
handler,
|
|
1066
|
-
options
|
|
1067
|
-
} = propsAtFulfillment;
|
|
1068
|
-
const cachedData = getEntry(handler, options);
|
|
1069
|
-
|
|
1070
|
-
if (!_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__["Server"].isServerSide() && (cachedData == null || handler.shouldRefreshCache(options, cachedData))) {
|
|
1071
|
-
/**
|
|
1072
|
-
* We're not on the server, the cache missed, or our handler says
|
|
1073
|
-
* we should refresh the cache.
|
|
1074
|
-
*
|
|
1075
|
-
* Therefore, we need to request data.
|
|
1076
|
-
*
|
|
1077
|
-
* We have to do this here from the constructor so that this
|
|
1078
|
-
* data request is tracked when performing server-side rendering.
|
|
1079
|
-
*/
|
|
1080
|
-
_util_request_fulfillment_js__WEBPACK_IMPORTED_MODULE_2__[/* RequestFulfillment */ "a"].Default.fulfill(handler, options).then(cacheEntry => {
|
|
1081
|
-
/**
|
|
1082
|
-
* We get here, we should have updated the cache.
|
|
1083
|
-
* However, we need to update the component, but we
|
|
1084
|
-
* should only do that if the props are the same as they
|
|
1085
|
-
* were when this was called.
|
|
1086
|
-
*/
|
|
1087
|
-
if (this._mounted && this._propsMatch(propsAtFulfillment)) {
|
|
1088
|
-
this.setState({
|
|
1089
|
-
loading: false,
|
|
1090
|
-
data: cacheEntry.data,
|
|
1091
|
-
error: cacheEntry.error
|
|
1092
|
-
});
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
return null;
|
|
1096
|
-
}).catch(e => {
|
|
1097
|
-
/**
|
|
1098
|
-
* We should never get here, but if we do.
|
|
1099
|
-
*/
|
|
1100
|
-
// eslint-disable-next-line no-console
|
|
1101
|
-
console.error(`Unexpected error occurred during data fulfillment: ${e}`);
|
|
1102
|
-
|
|
1103
|
-
if (this._mounted && this._propsMatch(propsAtFulfillment)) {
|
|
1104
|
-
this.setState({
|
|
1105
|
-
loading: false,
|
|
1106
|
-
data: null,
|
|
1107
|
-
error: typeof e === "string" ? e : e.message
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
return null;
|
|
1112
|
-
});
|
|
1113
|
-
}
|
|
1114
|
-
/**
|
|
1115
|
-
* This is the default response for the server and for the initial
|
|
1116
|
-
* client-side render if we have cachedData.
|
|
1117
|
-
*
|
|
1118
|
-
* This ensures we don't make promises we don't want when doing
|
|
1119
|
-
* server-side rendering. Instead, we either have data from the cache
|
|
1120
|
-
* or we don't.
|
|
1121
|
-
*/
|
|
1122
|
-
|
|
1123
|
-
|
|
924
|
+
const resultFromCacheEntry = cacheEntry => {
|
|
925
|
+
// No cache entry means we didn't load one yet.
|
|
926
|
+
if (cacheEntry == null) {
|
|
1124
927
|
return {
|
|
1125
|
-
|
|
1126
|
-
data: cachedData && cachedData.data,
|
|
1127
|
-
error: cachedData && cachedData.error
|
|
928
|
+
status: "loading"
|
|
1128
929
|
};
|
|
1129
930
|
}
|
|
1130
931
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
error
|
|
1136
|
-
} = this.state;
|
|
1137
|
-
|
|
1138
|
-
if (loading) {
|
|
1139
|
-
return {
|
|
1140
|
-
loading: true
|
|
1141
|
-
};
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
if (data != null) {
|
|
1145
|
-
return {
|
|
1146
|
-
loading: false,
|
|
1147
|
-
data
|
|
1148
|
-
};
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
if (error == null) {
|
|
1152
|
-
// We should never get here ever.
|
|
1153
|
-
throw new Error("Loaded result has invalid state where data and error are missing");
|
|
1154
|
-
}
|
|
932
|
+
const {
|
|
933
|
+
data,
|
|
934
|
+
error
|
|
935
|
+
} = cacheEntry;
|
|
1155
936
|
|
|
937
|
+
if (data != null) {
|
|
1156
938
|
return {
|
|
1157
|
-
|
|
1158
|
-
|
|
939
|
+
status: "success",
|
|
940
|
+
data
|
|
1159
941
|
};
|
|
1160
942
|
}
|
|
1161
943
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
_renderWithTrackingContext(result) {
|
|
1170
|
-
return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](_util_request_tracking_js__WEBPACK_IMPORTED_MODULE_3__[/* TrackerContext */ "b"].Consumer, null, track => {
|
|
1171
|
-
/**
|
|
1172
|
-
* If data tracking wasn't enabled, don't do it.
|
|
1173
|
-
*/
|
|
1174
|
-
if (track != null) {
|
|
1175
|
-
track(this.props.handler, this.props.options);
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
return this._renderContent(result);
|
|
1179
|
-
});
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
render() {
|
|
1183
|
-
const result = this._resultFromState(); // We only track data requests when we are server-side and we don't
|
|
1184
|
-
// already have a result. The existence of a result is indicated by the
|
|
1185
|
-
// loading flag being false.
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
if (result.loading && _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_1__["Server"].isServerSide()) {
|
|
1189
|
-
return this._renderWithTrackingContext(result);
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
return this._renderContent(result);
|
|
944
|
+
if (error == null) {
|
|
945
|
+
// We should never get here ever.
|
|
946
|
+
return {
|
|
947
|
+
status: "error",
|
|
948
|
+
error: "Loaded result has invalid state where data and error are missing"
|
|
949
|
+
};
|
|
1193
950
|
}
|
|
1194
951
|
|
|
1195
|
-
|
|
952
|
+
return {
|
|
953
|
+
status: "error",
|
|
954
|
+
error
|
|
955
|
+
};
|
|
956
|
+
};
|
|
1196
957
|
|
|
1197
958
|
/***/ }),
|
|
1198
|
-
/*
|
|
959
|
+
/* 13 */
|
|
1199
960
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
1200
961
|
|
|
1201
962
|
"use strict";
|
|
@@ -1205,9 +966,9 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
1205
966
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hasUnfulfilledRequests", function() { return hasUnfulfilledRequests; });
|
|
1206
967
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeFromCache", function() { return removeFromCache; });
|
|
1207
968
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "removeAllFromCache", function() { return removeAllFromCache; });
|
|
1208
|
-
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
|
|
969
|
+
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
1209
970
|
/* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_0__);
|
|
1210
|
-
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(
|
|
971
|
+
/* harmony import */ var _util_response_cache_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
|
|
1211
972
|
/* harmony import */ var _util_request_tracking_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
|
|
1212
973
|
/* harmony import */ var _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
|
|
1213
974
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "RequestHandler", function() { return _util_request_handler_js__WEBPACK_IMPORTED_MODULE_3__["a"]; });
|
|
@@ -1221,11 +982,8 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
1221
982
|
/* harmony import */ var _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(11);
|
|
1222
983
|
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "InterceptData", function() { return _components_intercept_data_js__WEBPACK_IMPORTED_MODULE_6__["a"]; });
|
|
1223
984
|
|
|
1224
|
-
/* harmony import */ var
|
|
1225
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "
|
|
1226
|
-
|
|
1227
|
-
/* harmony import */ var _util_no_cache_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(5);
|
|
1228
|
-
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "NoCache", function() { return _util_no_cache_js__WEBPACK_IMPORTED_MODULE_8__["a"]; });
|
|
985
|
+
/* harmony import */ var _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(5);
|
|
986
|
+
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "useData", function() { return _hooks_use_data_js__WEBPACK_IMPORTED_MODULE_7__["a"]; });
|
|
1229
987
|
|
|
1230
988
|
|
|
1231
989
|
|
|
@@ -1259,6 +1017,5 @@ const removeAllFromCache = (handler, predicate) => _util_response_cache_js__WEBP
|
|
|
1259
1017
|
|
|
1260
1018
|
|
|
1261
1019
|
|
|
1262
|
-
|
|
1263
1020
|
/***/ })
|
|
1264
1021
|
/******/ ]);
|