index.mjs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. import { useEffect, useLayoutEffect, useState, useRef, useCallback } from 'react';
  2. import useSWR from 'swr';
  3. /*! *****************************************************************************
  4. Copyright (c) Microsoft Corporation.
  5. Permission to use, copy, modify, and/or distribute this software for any
  6. purpose with or without fee is hereby granted.
  7. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  8. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  12. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. PERFORMANCE OF THIS SOFTWARE.
  14. ***************************************************************************** */
  15. var __assign = function() {
  16. __assign = Object.assign || function __assign(t) {
  17. for (var s, i = 1, n = arguments.length; i < n; i++) {
  18. s = arguments[i];
  19. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
  20. }
  21. return t;
  22. };
  23. return __assign.apply(this, arguments);
  24. };
  25. function __awaiter(thisArg, _arguments, P, generator) {
  26. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  27. return new (P || (P = Promise))(function (resolve, reject) {
  28. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  29. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  30. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  31. step((generator = generator.apply(thisArg, _arguments || [])).next());
  32. });
  33. }
  34. function __generator(thisArg, body) {
  35. var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
  36. return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
  37. function verb(n) { return function (v) { return step([n, v]); }; }
  38. function step(op) {
  39. if (f) throw new TypeError("Generator is already executing.");
  40. while (_) try {
  41. if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
  42. if (y = 0, t) op = [op[0] & 2, t.value];
  43. switch (op[0]) {
  44. case 0: case 1: t = op; break;
  45. case 4: _.label++; return { value: op[1], done: false };
  46. case 5: _.label++; y = op[1]; op = [0]; continue;
  47. case 7: op = _.ops.pop(); _.trys.pop(); continue;
  48. default:
  49. if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
  50. if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
  51. if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
  52. if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
  53. if (t[2]) _.ops.pop();
  54. _.trys.pop(); continue;
  55. }
  56. op = body.call(thisArg, _);
  57. } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
  58. if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
  59. }
  60. }
  61. var noop = function () { };
  62. // Using noop() as the undefined value as undefined can possibly be replaced
  63. // by something else. Prettier ignore and extra parentheses are necessary here
  64. // to ensure that tsc doesn't remove the __NOINLINE__ comment.
  65. // prettier-ignore
  66. var UNDEFINED = ( /*#__NOINLINE__*/noop());
  67. var OBJECT = Object;
  68. var isUndefined = function (v) { return v === UNDEFINED; };
  69. var isFunction = function (v) { return typeof v == 'function'; };
  70. var STR_UNDEFINED = 'undefined';
  71. // NOTE: Use function to guarantee it's re-evaluated between jsdom and node runtime for tests.
  72. var hasWindow = function () { return typeof window != STR_UNDEFINED; };
  73. var IS_SERVER = !hasWindow() || 'Deno' in window;
  74. // React currently throws a warning when using useLayoutEffect on the server.
  75. // To get around it, we can conditionally useEffect on the server (no-op) and
  76. // useLayoutEffect in the browser.
  77. var useIsomorphicLayoutEffect = IS_SERVER ? useEffect : useLayoutEffect;
  78. // use WeakMap to store the object->key mapping
  79. // so the objects can be garbage collected.
  80. // WeakMap uses a hashtable under the hood, so the lookup
  81. // complexity is almost O(1).
  82. var table = new WeakMap();
  83. // counter of the key
  84. var counter = 0;
  85. // A stable hash implementation that supports:
  86. // - Fast and ensures unique hash properties
  87. // - Handles unserializable values
  88. // - Handles object key ordering
  89. // - Generates short results
  90. //
  91. // This is not a serialization function, and the result is not guaranteed to be
  92. // parsible.
  93. var stableHash = function (arg) {
  94. var type = typeof arg;
  95. var constructor = arg && arg.constructor;
  96. var isDate = constructor == Date;
  97. var result;
  98. var index;
  99. if (OBJECT(arg) === arg && !isDate && constructor != RegExp) {
  100. // Object/function, not null/date/regexp. Use WeakMap to store the id first.
  101. // If it's already hashed, directly return the result.
  102. result = table.get(arg);
  103. if (result)
  104. return result;
  105. // Store the hash first for circular reference detection before entering the
  106. // recursive `stableHash` calls.
  107. // For other objects like set and map, we use this id directly as the hash.
  108. result = ++counter + '~';
  109. table.set(arg, result);
  110. if (constructor == Array) {
  111. // Array.
  112. result = '@';
  113. for (index = 0; index < arg.length; index++) {
  114. result += stableHash(arg[index]) + ',';
  115. }
  116. table.set(arg, result);
  117. }
  118. if (constructor == OBJECT) {
  119. // Object, sort keys.
  120. result = '#';
  121. var keys = OBJECT.keys(arg).sort();
  122. while (!isUndefined((index = keys.pop()))) {
  123. if (!isUndefined(arg[index])) {
  124. result += index + ':' + stableHash(arg[index]) + ',';
  125. }
  126. }
  127. table.set(arg, result);
  128. }
  129. }
  130. else {
  131. result = isDate
  132. ? arg.toJSON()
  133. : type == 'symbol'
  134. ? arg.toString()
  135. : type == 'string'
  136. ? JSON.stringify(arg)
  137. : '' + arg;
  138. }
  139. return result;
  140. };
  141. var serialize = function (key) {
  142. if (isFunction(key)) {
  143. try {
  144. key = key();
  145. }
  146. catch (err) {
  147. // dependencies not ready
  148. key = '';
  149. }
  150. }
  151. var args = [].concat(key);
  152. // If key is not falsy, or not an empty array, hash it.
  153. key =
  154. typeof key == 'string'
  155. ? key
  156. : (Array.isArray(key) ? key.length : key)
  157. ? stableHash(key)
  158. : '';
  159. var infoKey = key ? '$swr$' + key : '';
  160. return [key, args, infoKey];
  161. };
  162. var normalize = function (args) {
  163. return isFunction(args[1])
  164. ? [args[0], args[1], args[2] || {}]
  165. : [args[0], null, (args[1] === null ? args[2] : args[1]) || {}];
  166. };
  167. // Create a custom hook with a middleware
  168. var withMiddleware = function (useSWR, middleware) {
  169. return function () {
  170. var args = [];
  171. for (var _i = 0; _i < arguments.length; _i++) {
  172. args[_i] = arguments[_i];
  173. }
  174. var _a = normalize(args), key = _a[0], fn = _a[1], config = _a[2];
  175. var uses = (config.use || []).concat(middleware);
  176. return useSWR(key, fn, __assign(__assign({}, config), { use: uses }));
  177. };
  178. };
  179. // We have to several type castings here because `useSWRInfinite` is a special
  180. var INFINITE_PREFIX = '$inf$';
  181. var getFirstPageKey = function (getKey) {
  182. return serialize(getKey ? getKey(0, null) : null)[0];
  183. };
  184. var unstable_serialize = function (getKey) {
  185. return INFINITE_PREFIX + getFirstPageKey(getKey);
  186. };
  187. var infinite = (function (useSWRNext) {
  188. return function (getKey, fn, config) {
  189. var rerender = useState({})[1];
  190. var didMountRef = useRef(false);
  191. var dataRef = useRef();
  192. var cache = config.cache, _a = config.initialSize, initialSize = _a === void 0 ? 1 : _a, _b = config.revalidateAll, revalidateAll = _b === void 0 ? false : _b, _c = config.persistSize, persistSize = _c === void 0 ? false : _c, _d = config.revalidateFirstPage, revalidateFirstPage = _d === void 0 ? true : _d, _e = config.revalidateOnMount, revalidateOnMount = _e === void 0 ? false : _e;
  193. // The serialized key of the first page.
  194. var firstPageKey = null;
  195. try {
  196. firstPageKey = getFirstPageKey(getKey);
  197. }
  198. catch (err) {
  199. // not ready
  200. }
  201. // We use cache to pass extra info (context) to fetcher so it can be globally
  202. // shared. The key of the context data is based on the first page key.
  203. var contextCacheKey = null;
  204. // Page size is also cached to share the page data between hooks with the
  205. // same key.
  206. var pageSizeCacheKey = null;
  207. if (firstPageKey) {
  208. contextCacheKey = '$ctx$' + firstPageKey;
  209. pageSizeCacheKey = '$len$' + firstPageKey;
  210. }
  211. var resolvePageSize = useCallback(function () {
  212. var cachedPageSize = cache.get(pageSizeCacheKey);
  213. return isUndefined(cachedPageSize) ? initialSize : cachedPageSize;
  214. // `cache` isn't allowed to change during the lifecycle
  215. // eslint-disable-next-line react-hooks/exhaustive-deps
  216. }, [pageSizeCacheKey, initialSize]);
  217. // keep the last page size to restore it with the persistSize option
  218. var lastPageSizeRef = useRef(resolvePageSize());
  219. // When the page key changes, we reset the page size if it's not persisted
  220. useIsomorphicLayoutEffect(function () {
  221. if (!didMountRef.current) {
  222. didMountRef.current = true;
  223. return;
  224. }
  225. if (firstPageKey) {
  226. // If the key has been changed, we keep the current page size if persistSize is enabled
  227. cache.set(pageSizeCacheKey, persistSize ? lastPageSizeRef.current : initialSize);
  228. }
  229. // `initialSize` isn't allowed to change during the lifecycle
  230. // eslint-disable-next-line react-hooks/exhaustive-deps
  231. }, [firstPageKey]);
  232. // Needs to check didMountRef during mounting, not in the fetcher
  233. var shouldRevalidateOnMount = revalidateOnMount && !didMountRef.current;
  234. // Actual SWR hook to load all pages in one fetcher.
  235. var swr = useSWRNext(firstPageKey ? INFINITE_PREFIX + firstPageKey : null, function () { return __awaiter(void 0, void 0, void 0, function () {
  236. var _a, forceRevalidateAll, originalData, data, pageSize, previousPageData, i, _b, pageKey, pageArgs, pageData, shouldFetchPage;
  237. return __generator(this, function (_c) {
  238. switch (_c.label) {
  239. case 0:
  240. _a = cache.get(contextCacheKey) || [], forceRevalidateAll = _a[0], originalData = _a[1];
  241. data = [];
  242. pageSize = resolvePageSize();
  243. previousPageData = null;
  244. i = 0;
  245. _c.label = 1;
  246. case 1:
  247. if (!(i < pageSize)) return [3 /*break*/, 5];
  248. _b = serialize(getKey(i, previousPageData)), pageKey = _b[0], pageArgs = _b[1];
  249. if (!pageKey) {
  250. // `pageKey` is falsy, stop fetching new pages.
  251. return [3 /*break*/, 5];
  252. }
  253. pageData = cache.get(pageKey);
  254. shouldFetchPage = revalidateAll ||
  255. forceRevalidateAll ||
  256. isUndefined(pageData) ||
  257. (revalidateFirstPage && !i && !isUndefined(dataRef.current)) ||
  258. shouldRevalidateOnMount ||
  259. (originalData &&
  260. !isUndefined(originalData[i]) &&
  261. !config.compare(originalData[i], pageData));
  262. if (!(fn && shouldFetchPage)) return [3 /*break*/, 3];
  263. return [4 /*yield*/, fn.apply(void 0, pageArgs)];
  264. case 2:
  265. pageData = _c.sent();
  266. cache.set(pageKey, pageData);
  267. _c.label = 3;
  268. case 3:
  269. data.push(pageData);
  270. previousPageData = pageData;
  271. _c.label = 4;
  272. case 4:
  273. ++i;
  274. return [3 /*break*/, 1];
  275. case 5:
  276. // once we executed the data fetching based on the context, clear the context
  277. cache.delete(contextCacheKey);
  278. // return the data
  279. return [2 /*return*/, data];
  280. }
  281. });
  282. }); }, config);
  283. // update dataRef
  284. useIsomorphicLayoutEffect(function () {
  285. dataRef.current = swr.data;
  286. }, [swr.data]);
  287. var mutate = useCallback(function () {
  288. var args = [];
  289. for (var _i = 0; _i < arguments.length; _i++) {
  290. args[_i] = arguments[_i];
  291. }
  292. var data = args[0];
  293. // Default to true.
  294. var shouldRevalidate = args[1] !== false;
  295. // It is possible that the key is still falsy.
  296. if (!contextCacheKey)
  297. return;
  298. if (shouldRevalidate) {
  299. if (!isUndefined(data)) {
  300. // We only revalidate the pages that are changed
  301. var originalData = dataRef.current;
  302. cache.set(contextCacheKey, [false, originalData]);
  303. }
  304. else {
  305. // Calling `mutate()`, we revalidate all pages
  306. cache.set(contextCacheKey, [true]);
  307. }
  308. }
  309. return args.length ? swr.mutate(data, shouldRevalidate) : swr.mutate();
  310. },
  311. // swr.mutate is always the same reference
  312. // eslint-disable-next-line react-hooks/exhaustive-deps
  313. [contextCacheKey]);
  314. // Function to load pages data from the cache based on the page size.
  315. var resolvePagesFromCache = function (pageSize) {
  316. // return an array of page data
  317. var data = [];
  318. var previousPageData = null;
  319. for (var i = 0; i < pageSize; ++i) {
  320. var pageKey = serialize(getKey(i, previousPageData))[0];
  321. // Get the cached page data.
  322. var pageData = pageKey ? cache.get(pageKey) : UNDEFINED;
  323. // Return the current data if we can't get it from the cache.
  324. if (isUndefined(pageData))
  325. return dataRef.current;
  326. data.push(pageData);
  327. previousPageData = pageData;
  328. }
  329. // Return the data
  330. return data;
  331. };
  332. // Extend the SWR API
  333. var setSize = useCallback(function (arg) {
  334. // It is possible that the key is still falsy.
  335. if (!pageSizeCacheKey)
  336. return;
  337. var size;
  338. if (isFunction(arg)) {
  339. size = arg(resolvePageSize());
  340. }
  341. else if (typeof arg == 'number') {
  342. size = arg;
  343. }
  344. if (typeof size != 'number')
  345. return;
  346. cache.set(pageSizeCacheKey, size);
  347. lastPageSizeRef.current = size;
  348. rerender({});
  349. return mutate(resolvePagesFromCache(size));
  350. },
  351. // `cache` and `rerender` isn't allowed to change during the lifecycle
  352. // eslint-disable-next-line react-hooks/exhaustive-deps
  353. [pageSizeCacheKey, resolvePageSize, mutate]);
  354. // Use getter functions to avoid unnecessary re-renders caused by triggering
  355. // all the getters of the returned swr object.
  356. return {
  357. size: resolvePageSize(),
  358. setSize: setSize,
  359. mutate: mutate,
  360. get error() {
  361. return swr.error;
  362. },
  363. get data() {
  364. return swr.data;
  365. },
  366. get isValidating() {
  367. return swr.isValidating;
  368. }
  369. };
  370. };
  371. });
  372. var index = withMiddleware(useSWR, infinite);
  373. export { index as default, infinite, unstable_serialize };