@choksheak/react-utils 0.2.7 → 0.2.9
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/package.json +50 -2
- package/sharedQuery.cjs +146 -191
- package/sharedQuery.d.mts +72 -12
- package/sharedQuery.d.ts +69 -10
- package/sharedQuery.min.cjs +1 -1
- package/sharedQuery.min.cjs.map +1 -1
- package/sharedQuery.min.mjs +1 -1
- package/sharedQuery.min.mjs.map +1 -1
- package/sharedQuery.mjs +151 -191
- package/sharedState.cjs +50 -103
- package/sharedState.d.mts +12 -7
- package/sharedState.d.ts +10 -6
- package/sharedState.min.cjs +1 -1
- package/sharedState.min.cjs.map +1 -1
- package/sharedState.min.mjs +1 -1
- package/sharedState.min.mjs.map +1 -1
- package/sharedState.mjs +48 -102
- package/storage.cjs +67 -0
- package/storage.d.mts +19 -0
- package/storage.d.ts +16 -0
- package/storage.min.cjs +2 -0
- package/storage.min.cjs.map +1 -0
- package/storage.min.mjs +2 -0
- package/storage.min.mjs.map +1 -0
- package/storage.mjs +42 -0
- package/{utils/stringify.cjs → stringify.cjs} +1 -1
- package/stringify.min.cjs.map +1 -0
- package/stringify.min.mjs.map +1 -0
- package/{utils/stringify.mjs → stringify.mjs} +1 -1
- package/{utils/useDeepMemo.cjs → useDeepMemo.cjs} +1 -1
- package/useDeepMemo.d.mts +7 -0
- package/useDeepMemo.d.ts +5 -0
- package/useDeepMemo.min.cjs.map +1 -0
- package/useDeepMemo.min.mjs.map +1 -0
- package/{utils/useDeepMemo.mjs → useDeepMemo.mjs} +1 -1
- package/utils/storage.cjs +0 -162
- package/utils/storage.d.mts +0 -46
- package/utils/storage.d.ts +0 -44
- package/utils/storage.min.cjs +0 -2
- package/utils/storage.min.cjs.map +0 -1
- package/utils/storage.min.mjs +0 -2
- package/utils/storage.min.mjs.map +0 -1
- package/utils/storage.mjs +0 -134
- package/utils/stringify.min.cjs.map +0 -1
- package/utils/stringify.min.mjs.map +0 -1
- package/utils/useDeepMemo.d.mts +0 -4
- package/utils/useDeepMemo.d.ts +0 -2
- package/utils/useDeepMemo.min.cjs.map +0 -1
- package/utils/useDeepMemo.min.mjs.map +0 -1
- /package/{utils/stringify.d.mts → stringify.d.mts} +0 -0
- /package/{utils/stringify.d.ts → stringify.d.ts} +0 -0
- /package/{utils/stringify.min.cjs → stringify.min.cjs} +0 -0
- /package/{utils/stringify.min.mjs → stringify.min.mjs} +0 -0
- /package/{utils/useDeepMemo.min.cjs → useDeepMemo.min.cjs} +0 -0
- /package/{utils/useDeepMemo.min.mjs → useDeepMemo.min.mjs} +0 -0
package/sharedQuery.mjs
CHANGED
|
@@ -1,114 +1,60 @@
|
|
|
1
1
|
// src/sharedQuery.ts
|
|
2
2
|
import { MS_PER_DAY as MS_PER_DAY2 } from "@choksheak/ts-utils/timeConstants";
|
|
3
3
|
import getByteSize from "object-sizeof";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useMemo as useMemo2,
|
|
8
|
+
useRef,
|
|
9
|
+
useState as useState2
|
|
10
|
+
} from "react";
|
|
5
11
|
|
|
6
12
|
// src/sharedState.ts
|
|
7
13
|
import { MS_PER_DAY } from "@choksheak/ts-utils/timeConstants";
|
|
8
14
|
import { useSyncExternalStore } from "react";
|
|
9
15
|
|
|
10
|
-
// src/
|
|
16
|
+
// src/storage.ts
|
|
11
17
|
import { kvStore } from "@choksheak/ts-utils/kvStore";
|
|
18
|
+
import { LocalStore } from "@choksheak/ts-utils/localStore";
|
|
12
19
|
function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
13
|
-
const {
|
|
14
|
-
if (store) {
|
|
15
|
-
return
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
load(key) {
|
|
43
|
-
if (typeof window === "undefined") return void 0;
|
|
44
|
-
const fullKey = this.keyPrefix + ":" + key;
|
|
45
|
-
const raw = window.localStorage.getItem(fullKey);
|
|
46
|
-
if (!raw) return void 0;
|
|
47
|
-
try {
|
|
48
|
-
const box = JSON.parse(raw);
|
|
49
|
-
if (!box || box.expiresAt && Date.now() >= box.expiresAt) {
|
|
50
|
-
window.localStorage.removeItem(fullKey);
|
|
51
|
-
return void 0;
|
|
52
|
-
}
|
|
53
|
-
return box.value;
|
|
54
|
-
} catch {
|
|
55
|
-
window.localStorage.removeItem(fullKey);
|
|
56
|
-
return void 0;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
delete(key) {
|
|
60
|
-
if (typeof window === "undefined") return;
|
|
61
|
-
const fullKey = this.keyPrefix + ":" + key;
|
|
62
|
-
window.localStorage.removeItem(fullKey);
|
|
63
|
-
}
|
|
64
|
-
clear() {
|
|
65
|
-
if (typeof window === "undefined") return;
|
|
66
|
-
for (const key of Object.keys(window.localStorage)) {
|
|
67
|
-
if (key.startsWith(this.keyPrefix)) {
|
|
68
|
-
window.localStorage.removeItem(key);
|
|
20
|
+
const { store } = options ?? {};
|
|
21
|
+
if (!store) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
|
|
25
|
+
const expiryMs = store.expiryMs || defaultStoreExpiryMs;
|
|
26
|
+
switch (store.persistTo) {
|
|
27
|
+
case "localStorage":
|
|
28
|
+
return new LocalStore(store.key, {
|
|
29
|
+
defaultExpiryMs: expiryMs
|
|
30
|
+
}).asStorageAdapter();
|
|
31
|
+
case "indexedDb": {
|
|
32
|
+
const keyPrefix = store.key + ":";
|
|
33
|
+
return {
|
|
34
|
+
set: async (key, value) => {
|
|
35
|
+
await kvStore.set(keyPrefix + key, value, expiryMs);
|
|
36
|
+
},
|
|
37
|
+
get: async (key) => {
|
|
38
|
+
return await kvStore.get(keyPrefix + key);
|
|
39
|
+
},
|
|
40
|
+
delete: async (key) => {
|
|
41
|
+
await kvStore.delete(keyPrefix + key);
|
|
42
|
+
},
|
|
43
|
+
clear: async () => {
|
|
44
|
+
await kvStore.clear();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
69
47
|
}
|
|
48
|
+
default:
|
|
49
|
+
store.persistTo;
|
|
50
|
+
throw new Error(`Unknown store.adapter ${store.persistTo}`);
|
|
70
51
|
}
|
|
71
52
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
constructor(keyPrefix, expiryMs) {
|
|
75
|
-
this.keyPrefix = keyPrefix;
|
|
76
|
-
this.expiryMs = expiryMs;
|
|
77
|
-
}
|
|
78
|
-
async save(key, value) {
|
|
79
|
-
if (typeof window === "undefined") return;
|
|
80
|
-
const fullKey = this.keyPrefix + ":" + key;
|
|
81
|
-
await kvStore.set(fullKey, JSON.stringify(value), this.expiryMs);
|
|
82
|
-
}
|
|
83
|
-
async load(key) {
|
|
84
|
-
if (typeof window === "undefined") return void 0;
|
|
85
|
-
const fullKey = this.keyPrefix + ":" + key;
|
|
86
|
-
const raw = await kvStore.get(fullKey);
|
|
87
|
-
if (raw === void 0) return void 0;
|
|
88
|
-
try {
|
|
89
|
-
return JSON.parse(raw);
|
|
90
|
-
} catch {
|
|
91
|
-
await kvStore.delete(fullKey);
|
|
92
|
-
return void 0;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
async delete(key) {
|
|
96
|
-
if (typeof window === "undefined") return;
|
|
97
|
-
const fullKey = this.keyPrefix + ":" + key;
|
|
98
|
-
await kvStore.delete(fullKey);
|
|
99
|
-
}
|
|
100
|
-
async clear() {
|
|
101
|
-
if (typeof window === "undefined") return;
|
|
102
|
-
await kvStore.forEach(async (k) => {
|
|
103
|
-
if (k.startsWith(this.keyPrefix)) {
|
|
104
|
-
await kvStore.delete(k);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
};
|
|
53
|
+
return store;
|
|
54
|
+
}
|
|
109
55
|
|
|
110
56
|
// src/sharedState.ts
|
|
111
|
-
var
|
|
57
|
+
var SharedStateConfig = {
|
|
112
58
|
/**
|
|
113
59
|
* Default duration for storage expiration in milliseconds. Initial value
|
|
114
60
|
* is 30 days, but can be changed to anything.
|
|
@@ -175,12 +121,14 @@ var SharedState = class {
|
|
|
175
121
|
this.pubSubKey = String(stateKey++);
|
|
176
122
|
this.storageAdapter = getStorageAdapter(
|
|
177
123
|
options,
|
|
178
|
-
|
|
124
|
+
SharedStateConfig.storeExpiryMs
|
|
179
125
|
);
|
|
180
|
-
this.setValueBounded = (
|
|
126
|
+
this.setValueBounded = (next) => {
|
|
127
|
+
this.setValue(next);
|
|
128
|
+
};
|
|
181
129
|
pubSubStore.setNoNotify(this.pubSubKey, defaultValue);
|
|
182
130
|
const lazyLoad = options?.lazyLoad;
|
|
183
|
-
const lazy = lazyLoad !== void 0 ? lazyLoad :
|
|
131
|
+
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
184
132
|
if (!lazy) {
|
|
185
133
|
void this.initDefaultValueOnce();
|
|
186
134
|
}
|
|
@@ -205,19 +153,19 @@ var SharedState = class {
|
|
|
205
153
|
next = next(prev);
|
|
206
154
|
}
|
|
207
155
|
pubSubStore.set(this.pubSubKey, next);
|
|
208
|
-
void this.storageAdapter?.
|
|
156
|
+
void this.storageAdapter?.set(STORAGE_KEY, next);
|
|
209
157
|
}
|
|
210
158
|
// Remove the value of the shared state. Actually this just sets the value
|
|
211
159
|
// back to the given default value.
|
|
212
160
|
delete() {
|
|
213
161
|
pubSubStore.set(this.pubSubKey, this.defaultValue);
|
|
214
|
-
void this.storageAdapter?.
|
|
162
|
+
void this.storageAdapter?.set(STORAGE_KEY, this.defaultValue);
|
|
215
163
|
}
|
|
216
164
|
// Initial default handling
|
|
217
165
|
async initDefaultValueOnce() {
|
|
218
166
|
if (this.initDone) return;
|
|
219
167
|
this.initDone = true;
|
|
220
|
-
const v = await this.storageAdapter?.
|
|
168
|
+
const v = await this.storageAdapter?.get(STORAGE_KEY);
|
|
221
169
|
pubSubStore.set(this.pubSubKey, v !== void 0 ? v : this.defaultValue);
|
|
222
170
|
}
|
|
223
171
|
};
|
|
@@ -235,7 +183,7 @@ function useSharedState(state) {
|
|
|
235
183
|
return [value, state.setValueBounded];
|
|
236
184
|
}
|
|
237
185
|
|
|
238
|
-
// src/
|
|
186
|
+
// src/stringify.ts
|
|
239
187
|
function stringifyDeterministicForKeys(params) {
|
|
240
188
|
const cache = /* @__PURE__ */ new Set();
|
|
241
189
|
return JSON.stringify(params, (_, value) => {
|
|
@@ -266,7 +214,7 @@ function stringifyDeterministicForKeys(params) {
|
|
|
266
214
|
});
|
|
267
215
|
}
|
|
268
216
|
|
|
269
|
-
// src/
|
|
217
|
+
// src/useDeepMemo.ts
|
|
270
218
|
import isEqual from "lodash/isEqual";
|
|
271
219
|
import { useMemo, useState } from "react";
|
|
272
220
|
function useDeepMemo(value) {
|
|
@@ -281,22 +229,25 @@ function useDeepMemo(value) {
|
|
|
281
229
|
}
|
|
282
230
|
|
|
283
231
|
// src/sharedQuery.ts
|
|
284
|
-
var
|
|
285
|
-
|
|
232
|
+
var SharedQueryConfig = {
|
|
233
|
+
/** Refetch on every page load. */
|
|
286
234
|
staleMs: 0,
|
|
287
|
-
|
|
235
|
+
/** Keep in cache for a long time. */
|
|
288
236
|
expiryMs: 30 * MS_PER_DAY2,
|
|
289
|
-
|
|
237
|
+
/** Always refetch automatically if the data is stale. */
|
|
290
238
|
refetchOnStale: true,
|
|
291
|
-
|
|
239
|
+
/** No default persistence configured. */
|
|
292
240
|
persistTo: void 0,
|
|
293
|
-
|
|
241
|
+
/** 100 is not too large, but please tweak accordingly. */
|
|
294
242
|
maxSize: 100,
|
|
295
|
-
|
|
243
|
+
/** 100kb limit before discarding old records. */
|
|
296
244
|
maxBytes: 1e5,
|
|
297
|
-
|
|
245
|
+
/** Default to log to console. */
|
|
298
246
|
log: (...args) => console.log("[sharedQuery]", ...args)
|
|
299
247
|
};
|
|
248
|
+
function configureSharedQuery(config) {
|
|
249
|
+
Object.assign(SharedQueryConfig, config);
|
|
250
|
+
}
|
|
300
251
|
var SharedQuery = class {
|
|
301
252
|
inflightQueries = /* @__PURE__ */ new Map();
|
|
302
253
|
queryState;
|
|
@@ -313,32 +264,32 @@ var SharedQuery = class {
|
|
|
313
264
|
refetchOnStale;
|
|
314
265
|
maxSize;
|
|
315
266
|
maxBytes;
|
|
267
|
+
keepLastNonEmptyData;
|
|
316
268
|
log;
|
|
317
269
|
constructor(options) {
|
|
318
270
|
this.queryName = options.queryName;
|
|
319
271
|
this.queryFn = options.queryFn;
|
|
320
|
-
this.staleMs = options?.staleMs ??
|
|
321
|
-
this.expiryMs = options?.expiryMs ??
|
|
322
|
-
this.refetchOnStale = options?.refetchOnStale ??
|
|
323
|
-
if (!options.
|
|
324
|
-
const persistTo = options.persistTo ??
|
|
325
|
-
if (persistTo
|
|
326
|
-
options.
|
|
327
|
-
|
|
328
|
-
|
|
272
|
+
this.staleMs = options?.staleMs ?? SharedQueryConfig.staleMs;
|
|
273
|
+
this.expiryMs = options?.expiryMs ?? SharedQueryConfig.expiryMs;
|
|
274
|
+
this.refetchOnStale = options?.refetchOnStale ?? SharedQueryConfig.refetchOnStale;
|
|
275
|
+
if (!options.store) {
|
|
276
|
+
const persistTo = options.persistTo ?? SharedQueryConfig.persistTo;
|
|
277
|
+
if (persistTo) {
|
|
278
|
+
options.store = {
|
|
279
|
+
persistTo,
|
|
280
|
+
key: options.queryName,
|
|
281
|
+
expiryMs: this.expiryMs
|
|
282
|
+
};
|
|
329
283
|
}
|
|
330
284
|
}
|
|
331
|
-
this.maxSize = options?.maxSize ??
|
|
332
|
-
this.maxBytes = options?.maxBytes ??
|
|
333
|
-
this.
|
|
285
|
+
this.maxSize = options?.maxSize ?? SharedQueryConfig.maxSize;
|
|
286
|
+
this.maxBytes = options?.maxBytes ?? SharedQueryConfig.maxBytes;
|
|
287
|
+
this.keepLastNonEmptyData = Boolean(options?.keepLastNonEmptyData);
|
|
288
|
+
this.log = options?.log ?? SharedQueryConfig.log;
|
|
334
289
|
this.queryState = sharedState(
|
|
335
290
|
{},
|
|
336
291
|
// defaultValue
|
|
337
|
-
|
|
338
|
-
...options,
|
|
339
|
-
// Fallback to use expiryMs for the storeExpiryMs.
|
|
340
|
-
storeExpiryMs: options?.storeExpiryMs || this.expiryMs
|
|
341
|
-
}
|
|
292
|
+
options
|
|
342
293
|
);
|
|
343
294
|
}
|
|
344
295
|
/** Returns a key to identify requests based on the given args. */
|
|
@@ -449,7 +400,14 @@ var SharedQuery = class {
|
|
|
449
400
|
}
|
|
450
401
|
/** Get the current stored data for a query key. */
|
|
451
402
|
getData(queryKey) {
|
|
452
|
-
|
|
403
|
+
const stateValue = this.queryState.getSnapshot()?.[queryKey];
|
|
404
|
+
if (stateValue?.lastUpdatedMs) {
|
|
405
|
+
if (this.isExpired(stateValue.lastUpdatedMs)) {
|
|
406
|
+
this.deleteData(queryKey);
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return stateValue?.data;
|
|
453
411
|
}
|
|
454
412
|
/** Set the data directly if the user obtained it from somewhere else. */
|
|
455
413
|
setData(queryKey, data, dataUpdatedMs = Date.now()) {
|
|
@@ -519,70 +477,64 @@ var SharedQuery = class {
|
|
|
519
477
|
return;
|
|
520
478
|
}
|
|
521
479
|
const oldByteSize = getByteSize(oldState);
|
|
522
|
-
let
|
|
480
|
+
let numToCut = this.maxSize ? oldSize - this.maxSize : 0;
|
|
481
|
+
let bytesToCut = this.maxBytes ? oldByteSize - this.maxBytes : 0;
|
|
482
|
+
if (numToCut <= 0 && bytesToCut <= 0) {
|
|
483
|
+
this.log(
|
|
484
|
+
`No need to trim data for ${this.queryName}: size=${oldSize} (limit=${this.maxSize}), byteSize=${oldByteSize.toLocaleString()} (limit=${this.maxBytes.toLocaleString()})`
|
|
485
|
+
);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
this.log(
|
|
489
|
+
`Need to trim ${this.queryName}: numToCut=${numToCut}, bytesToCut=${bytesToCut}`
|
|
490
|
+
);
|
|
491
|
+
const newState = { ...oldState };
|
|
523
492
|
let needUpdate = false;
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
delete newState[key];
|
|
545
|
-
numToCut--;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
493
|
+
const deleteKeyIfNotMounted = (key, expired) => {
|
|
494
|
+
if (this.mountedKeys.has(key)) {
|
|
495
|
+
this.log(`Cannot clean up ${key} as it is mounted`);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const byteSize = getByteSize(key) + getByteSize(newState[key]);
|
|
499
|
+
this.log(
|
|
500
|
+
`Cleaning up unmounted${expired ? ", expired" : ""} ${key} (byteSize=${byteSize.toLocaleString()})`
|
|
501
|
+
);
|
|
502
|
+
numToCut--;
|
|
503
|
+
bytesToCut -= byteSize;
|
|
504
|
+
delete newState[key];
|
|
505
|
+
needUpdate = true;
|
|
506
|
+
};
|
|
507
|
+
let entriesByTimeAscending = Object.entries(newState).sort(
|
|
508
|
+
(entry1, entry2) => entry1[1].lastUpdatedMs - entry2[1].lastUpdatedMs
|
|
509
|
+
);
|
|
510
|
+
for (const [key] of entriesByTimeAscending) {
|
|
511
|
+
if (!this.isExpired(newState[key].lastUpdatedMs)) {
|
|
512
|
+
break;
|
|
548
513
|
}
|
|
514
|
+
deleteKeyIfNotMounted(key, true);
|
|
549
515
|
}
|
|
550
|
-
if (
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
if (bytesToCut > 0) {
|
|
554
|
-
this.log(
|
|
555
|
-
`enforceSizeLimit for ${this.queryName} needed due to byte size ${currentByteSize.toLocaleString()} > ${this.maxBytes.toLocaleString()}`
|
|
556
|
-
);
|
|
557
|
-
if (!needUpdate) {
|
|
558
|
-
newState = { ...newState };
|
|
559
|
-
needUpdate = true;
|
|
560
|
-
}
|
|
561
|
-
const entriesByTimeAscending = Object.entries(newState).sort(
|
|
516
|
+
if (numToCut > 0 || bytesToCut > 0) {
|
|
517
|
+
if (needUpdate) {
|
|
518
|
+
entriesByTimeAscending = Object.entries(newState).sort(
|
|
562
519
|
(entry1, entry2) => entry1[1].lastUpdatedMs - entry2[1].lastUpdatedMs
|
|
563
520
|
);
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
this.log(`Cleaning up unmounted ${key}`);
|
|
570
|
-
const thisSize = getByteSize(key) + getByteSize(newState[key]);
|
|
571
|
-
delete newState[key];
|
|
572
|
-
bytesToCut -= thisSize;
|
|
573
|
-
}
|
|
521
|
+
}
|
|
522
|
+
for (const [key] of entriesByTimeAscending) {
|
|
523
|
+
deleteKeyIfNotMounted(key, false);
|
|
524
|
+
if (numToCut <= 0 && bytesToCut <= 0) {
|
|
525
|
+
break;
|
|
574
526
|
}
|
|
575
527
|
}
|
|
576
528
|
}
|
|
577
|
-
if (needUpdate) {
|
|
578
|
-
this.queryState.setValue(newState);
|
|
529
|
+
if (!needUpdate) {
|
|
579
530
|
this.log(
|
|
580
|
-
`
|
|
531
|
+
`No trimmable entries found for ${this.queryName}: size=${oldSize} (limit=${this.maxSize}), byteSize=${oldByteSize.toLocaleString()} (limit=${this.maxBytes.toLocaleString()})`
|
|
581
532
|
);
|
|
582
533
|
return;
|
|
583
534
|
}
|
|
535
|
+
this.queryState.setValue(newState);
|
|
584
536
|
this.log(
|
|
585
|
-
`
|
|
537
|
+
`Trimmed data for ${this.queryName}: size=[${oldSize} -> ${Object.keys(newState).length}] (limit=${this.maxSize}), byteSize=[${oldByteSize.toLocaleString()} -> ${getByteSize(newState).toLocaleString()}] (limit=${this.maxBytes.toLocaleString()})`
|
|
586
538
|
);
|
|
587
539
|
}
|
|
588
540
|
};
|
|
@@ -598,7 +550,7 @@ var DEFAULT_QUERY_STATE_ENTRY = {
|
|
|
598
550
|
lastUpdatedMs: 0,
|
|
599
551
|
loading: true
|
|
600
552
|
};
|
|
601
|
-
function useSharedQuery(query, args
|
|
553
|
+
function useSharedQuery(query, ...args) {
|
|
602
554
|
const stableArgs = useDeepMemo(args);
|
|
603
555
|
const [queryState, setQueryState] = useSharedState(query.queryState);
|
|
604
556
|
const isMounted = useRef(true);
|
|
@@ -606,23 +558,29 @@ function useSharedQuery(query, args = []) {
|
|
|
606
558
|
() => query.getQueryKey(stableArgs),
|
|
607
559
|
[query, stableArgs]
|
|
608
560
|
);
|
|
609
|
-
const queryStateValue = queryState[queryKey];
|
|
561
|
+
const queryStateValue = queryState[queryKey] ?? DEFAULT_QUERY_STATE_ENTRY;
|
|
610
562
|
const setQueryStateValue = useCallback(
|
|
611
|
-
(
|
|
563
|
+
(next) => {
|
|
612
564
|
setQueryState((prev) => {
|
|
613
565
|
const clone = { ...prev };
|
|
614
|
-
clone[queryKey] = typeof
|
|
566
|
+
clone[queryKey] = typeof next === "function" ? next(clone[queryKey] ?? DEFAULT_QUERY_STATE_ENTRY) : next;
|
|
615
567
|
return clone;
|
|
616
568
|
});
|
|
617
569
|
},
|
|
618
570
|
[queryKey, setQueryState]
|
|
619
571
|
);
|
|
572
|
+
const [lastNonEmptyData, setLastNonEmptyData] = useState2();
|
|
573
|
+
useEffect(() => {
|
|
574
|
+
if (query.keepLastNonEmptyData && queryStateValue.data !== void 0) {
|
|
575
|
+
setLastNonEmptyData(queryStateValue.data);
|
|
576
|
+
}
|
|
577
|
+
}, [query.keepLastNonEmptyData, queryStateValue.data]);
|
|
620
578
|
const execute = useCallback(
|
|
621
579
|
async (forceRefresh) => {
|
|
622
580
|
query.log(`Begin executing shared query ${queryKey}`);
|
|
623
581
|
setQueryStateValue((prev) => ({
|
|
624
582
|
// Keep old data and error.
|
|
625
|
-
...prev
|
|
583
|
+
...prev,
|
|
626
584
|
lastUpdatedMs: Date.now(),
|
|
627
585
|
loading: true
|
|
628
586
|
}));
|
|
@@ -643,7 +601,7 @@ function useSharedQuery(query, args = []) {
|
|
|
643
601
|
const now = Date.now();
|
|
644
602
|
setQueryStateValue((prev) => ({
|
|
645
603
|
// Keep old data.
|
|
646
|
-
...prev
|
|
604
|
+
...prev,
|
|
647
605
|
lastUpdatedMs: now,
|
|
648
606
|
loading: false,
|
|
649
607
|
error: e,
|
|
@@ -664,9 +622,9 @@ function useSharedQuery(query, args = []) {
|
|
|
664
622
|
};
|
|
665
623
|
}, [execute, query, queryKey]);
|
|
666
624
|
return useMemo2(() => {
|
|
667
|
-
const state = queryStateValue ?? DEFAULT_QUERY_STATE_ENTRY;
|
|
668
625
|
return {
|
|
669
|
-
...
|
|
626
|
+
...queryStateValue,
|
|
627
|
+
lastNonEmptyData,
|
|
670
628
|
// This works only if the user-given queryFn supports abort. If not,
|
|
671
629
|
// this function doesn't do anything, since we don't have any means to
|
|
672
630
|
// abort the running queryFn.
|
|
@@ -681,7 +639,7 @@ function useSharedQuery(query, args = []) {
|
|
|
681
639
|
setData: (data, dataUpdatedMs) => {
|
|
682
640
|
const now = Date.now();
|
|
683
641
|
setQueryStateValue((prev) => ({
|
|
684
|
-
...prev
|
|
642
|
+
...prev,
|
|
685
643
|
data,
|
|
686
644
|
dataUpdatedMs: dataUpdatedMs ?? now,
|
|
687
645
|
lastUpdatedMs: now,
|
|
@@ -698,6 +656,7 @@ function useSharedQuery(query, args = []) {
|
|
|
698
656
|
};
|
|
699
657
|
}, [
|
|
700
658
|
execute,
|
|
659
|
+
lastNonEmptyData,
|
|
701
660
|
query,
|
|
702
661
|
queryKey,
|
|
703
662
|
queryStateValue,
|
|
@@ -707,7 +666,8 @@ function useSharedQuery(query, args = []) {
|
|
|
707
666
|
}
|
|
708
667
|
export {
|
|
709
668
|
SharedQuery,
|
|
710
|
-
|
|
669
|
+
SharedQueryConfig,
|
|
670
|
+
configureSharedQuery,
|
|
711
671
|
sharedQuery,
|
|
712
672
|
useSharedQuery
|
|
713
673
|
};
|