@objectstack/client-react 17.0.0-rc.1 → 17.0.0-rc.2
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 +223 -0
- package/dist/index.d.mts +72 -2
- package/dist/index.d.ts +72 -2
- package/dist/index.js +162 -84
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +134 -58
- package/dist/index.mjs.map +1 -1
- package/package.json +14 -6
package/dist/index.mjs
CHANGED
|
@@ -25,14 +25,33 @@ function useClient() {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// src/data-hooks.tsx
|
|
28
|
-
import { useState, useEffect, useCallback, useRef as
|
|
28
|
+
import { useState, useEffect as useEffect2, useCallback as useCallback2, useRef as useRef3 } from "react";
|
|
29
|
+
|
|
30
|
+
// src/internal-deps.ts
|
|
31
|
+
import { useCallback, useEffect, useRef as useRef2 } from "react";
|
|
32
|
+
function stableKey(input) {
|
|
33
|
+
if (input === null || typeof input !== "object") return JSON.stringify(input) ?? "undefined";
|
|
34
|
+
if (Array.isArray(input)) return "[" + input.map(stableKey).join(",") + "]";
|
|
35
|
+
const obj = input;
|
|
36
|
+
const keys = Object.keys(obj).sort();
|
|
37
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + stableKey(obj[k])).join(",") + "}";
|
|
38
|
+
}
|
|
39
|
+
function useEventCallback(fn) {
|
|
40
|
+
const ref = useRef2(fn);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
ref.current = fn;
|
|
43
|
+
}, [fn]);
|
|
44
|
+
return useCallback((...args) => ref.current?.(...args), []);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/data-hooks.tsx
|
|
29
48
|
function useQuery(object, options = {}) {
|
|
30
49
|
const client = useClient();
|
|
31
50
|
const [data, setData] = useState(null);
|
|
32
51
|
const [isLoading, setIsLoading] = useState(true);
|
|
33
52
|
const [isRefetching, setIsRefetching] = useState(false);
|
|
34
53
|
const [error, setError] = useState(null);
|
|
35
|
-
const intervalRef =
|
|
54
|
+
const intervalRef = useRef3(void 0);
|
|
36
55
|
const {
|
|
37
56
|
query,
|
|
38
57
|
// Canonical names take precedence over legacy names
|
|
@@ -51,7 +70,17 @@ function useQuery(object, options = {}) {
|
|
|
51
70
|
const resolvedSort = orderBy;
|
|
52
71
|
const resolvedLimit = limit;
|
|
53
72
|
const resolvedOffset = offset;
|
|
54
|
-
const
|
|
73
|
+
const queryKey = stableKey({
|
|
74
|
+
query,
|
|
75
|
+
where: resolvedWhere,
|
|
76
|
+
fields: resolvedFields,
|
|
77
|
+
orderBy: resolvedSort,
|
|
78
|
+
limit: resolvedLimit,
|
|
79
|
+
offset: resolvedOffset
|
|
80
|
+
});
|
|
81
|
+
const handleSuccess = useEventCallback(onSuccess);
|
|
82
|
+
const handleError = useEventCallback(onError);
|
|
83
|
+
const fetchData = useCallback2(async (isRefetch = false) => {
|
|
55
84
|
if (!enabled) return;
|
|
56
85
|
try {
|
|
57
86
|
if (isRefetch) {
|
|
@@ -73,20 +102,20 @@ function useQuery(object, options = {}) {
|
|
|
73
102
|
});
|
|
74
103
|
}
|
|
75
104
|
setData(result);
|
|
76
|
-
|
|
105
|
+
handleSuccess(result);
|
|
77
106
|
} catch (err) {
|
|
78
107
|
const error2 = err instanceof Error ? err : new Error("Query failed");
|
|
79
108
|
setError(error2);
|
|
80
|
-
|
|
109
|
+
handleError(error2);
|
|
81
110
|
} finally {
|
|
82
111
|
setIsLoading(false);
|
|
83
112
|
setIsRefetching(false);
|
|
84
113
|
}
|
|
85
|
-
}, [client, object,
|
|
86
|
-
|
|
114
|
+
}, [client, object, queryKey, enabled, handleSuccess, handleError]);
|
|
115
|
+
useEffect2(() => {
|
|
87
116
|
fetchData();
|
|
88
117
|
}, [fetchData]);
|
|
89
|
-
|
|
118
|
+
useEffect2(() => {
|
|
90
119
|
if (refetchInterval && enabled) {
|
|
91
120
|
intervalRef.current = setInterval(() => {
|
|
92
121
|
fetchData(true);
|
|
@@ -99,7 +128,7 @@ function useQuery(object, options = {}) {
|
|
|
99
128
|
}
|
|
100
129
|
return void 0;
|
|
101
130
|
}, [refetchInterval, enabled, fetchData]);
|
|
102
|
-
const refetch =
|
|
131
|
+
const refetch = useCallback2(async () => {
|
|
103
132
|
await fetchData(true);
|
|
104
133
|
}, [fetchData]);
|
|
105
134
|
return {
|
|
@@ -116,7 +145,7 @@ function useMutation(object, operation, options = {}) {
|
|
|
116
145
|
const [isLoading, setIsLoading] = useState(false);
|
|
117
146
|
const [error, setError] = useState(null);
|
|
118
147
|
const { onSuccess, onError, onSettled } = options;
|
|
119
|
-
const mutateAsync =
|
|
148
|
+
const mutateAsync = useCallback2(async (variables) => {
|
|
120
149
|
setIsLoading(true);
|
|
121
150
|
setError(null);
|
|
122
151
|
try {
|
|
@@ -161,12 +190,12 @@ function useMutation(object, operation, options = {}) {
|
|
|
161
190
|
setIsLoading(false);
|
|
162
191
|
}
|
|
163
192
|
}, [client, object, operation, onSuccess, onError, onSettled]);
|
|
164
|
-
const mutate =
|
|
193
|
+
const mutate = useCallback2((variables) => {
|
|
165
194
|
return mutateAsync(variables).catch(() => {
|
|
166
195
|
return null;
|
|
167
196
|
});
|
|
168
197
|
}, [mutateAsync]);
|
|
169
|
-
const reset =
|
|
198
|
+
const reset = useCallback2(() => {
|
|
170
199
|
setData(null);
|
|
171
200
|
setError(null);
|
|
172
201
|
setIsLoading(false);
|
|
@@ -192,17 +221,17 @@ function usePagination(object, options = {}) {
|
|
|
192
221
|
const totalPages = Math.ceil(totalCount / pageSize);
|
|
193
222
|
const hasNextPage = page < totalPages;
|
|
194
223
|
const hasPreviousPage = page > 1;
|
|
195
|
-
const nextPage =
|
|
224
|
+
const nextPage = useCallback2(() => {
|
|
196
225
|
if (hasNextPage) {
|
|
197
226
|
setPage((p) => p + 1);
|
|
198
227
|
}
|
|
199
228
|
}, [hasNextPage]);
|
|
200
|
-
const previousPage =
|
|
229
|
+
const previousPage = useCallback2(() => {
|
|
201
230
|
if (hasPreviousPage) {
|
|
202
231
|
setPage((p) => p - 1);
|
|
203
232
|
}
|
|
204
233
|
}, [hasPreviousPage]);
|
|
205
|
-
const goToPage =
|
|
234
|
+
const goToPage = useCallback2((newPage) => {
|
|
206
235
|
const clampedPage = Math.max(1, Math.min(newPage, totalPages));
|
|
207
236
|
setPage(clampedPage);
|
|
208
237
|
}, [totalPages]);
|
|
@@ -235,12 +264,21 @@ function useInfiniteQuery(object, options = {}) {
|
|
|
235
264
|
const resolvedFields = fields;
|
|
236
265
|
const resolvedWhere = where;
|
|
237
266
|
const resolvedSort = orderBy;
|
|
267
|
+
const queryKey = stableKey({
|
|
268
|
+
query,
|
|
269
|
+
where: resolvedWhere,
|
|
270
|
+
fields: resolvedFields,
|
|
271
|
+
orderBy: resolvedSort,
|
|
272
|
+
pageSize
|
|
273
|
+
});
|
|
274
|
+
const handleSuccess = useEventCallback(onSuccess);
|
|
275
|
+
const handleError = useEventCallback(onError);
|
|
238
276
|
const [pages, setPages] = useState([]);
|
|
239
277
|
const [isLoading, setIsLoading] = useState(true);
|
|
240
278
|
const [isFetchingNextPage, setIsFetchingNextPage] = useState(false);
|
|
241
279
|
const [error, setError] = useState(null);
|
|
242
280
|
const [hasNextPage, setHasNextPage] = useState(true);
|
|
243
|
-
const fetchPage =
|
|
281
|
+
const fetchPage = useCallback2(async (skip, isNextPage = false) => {
|
|
244
282
|
try {
|
|
245
283
|
if (isNextPage) {
|
|
246
284
|
setIsFetchingNextPage(true);
|
|
@@ -272,27 +310,27 @@ function useInfiniteQuery(object, options = {}) {
|
|
|
272
310
|
const fetchedCount = result.records?.length ?? 0;
|
|
273
311
|
const hasMore = fetchedCount === pageSize;
|
|
274
312
|
setHasNextPage(hasMore);
|
|
275
|
-
|
|
313
|
+
handleSuccess(result);
|
|
276
314
|
} catch (err) {
|
|
277
315
|
const error2 = err instanceof Error ? err : new Error("Query failed");
|
|
278
316
|
setError(error2);
|
|
279
|
-
|
|
317
|
+
handleError(error2);
|
|
280
318
|
} finally {
|
|
281
319
|
setIsLoading(false);
|
|
282
320
|
setIsFetchingNextPage(false);
|
|
283
321
|
}
|
|
284
|
-
}, [client, object,
|
|
285
|
-
|
|
322
|
+
}, [client, object, queryKey, handleSuccess, handleError]);
|
|
323
|
+
useEffect2(() => {
|
|
286
324
|
if (enabled) {
|
|
287
325
|
fetchPage(0);
|
|
288
326
|
}
|
|
289
327
|
}, [enabled, fetchPage]);
|
|
290
|
-
const fetchNextPage =
|
|
328
|
+
const fetchNextPage = useCallback2(async () => {
|
|
291
329
|
if (!hasNextPage || isFetchingNextPage) return;
|
|
292
330
|
const nextSkip = pages.length * pageSize;
|
|
293
331
|
await fetchPage(nextSkip, true);
|
|
294
332
|
}, [hasNextPage, isFetchingNextPage, pages.length, pageSize, fetchPage]);
|
|
295
|
-
const refetch =
|
|
333
|
+
const refetch = useCallback2(async () => {
|
|
296
334
|
setPages([]);
|
|
297
335
|
await fetchPage(0);
|
|
298
336
|
}, [fetchPage]);
|
|
@@ -310,7 +348,7 @@ function useInfiniteQuery(object, options = {}) {
|
|
|
310
348
|
}
|
|
311
349
|
|
|
312
350
|
// src/metadata-hooks.tsx
|
|
313
|
-
import { useState as useState2, useEffect as
|
|
351
|
+
import { useState as useState2, useEffect as useEffect3, useCallback as useCallback3, useRef as useRef4 } from "react";
|
|
314
352
|
function useObject(objectName, options = {}) {
|
|
315
353
|
const client = useClient();
|
|
316
354
|
const locale = useObjectStackLocale();
|
|
@@ -327,7 +365,15 @@ function useObject(objectName, options = {}) {
|
|
|
327
365
|
onSuccess,
|
|
328
366
|
onError
|
|
329
367
|
} = options;
|
|
330
|
-
const
|
|
368
|
+
const dataRef = useRef4(data);
|
|
369
|
+
const etagRef = useRef4(etag);
|
|
370
|
+
useEffect3(() => {
|
|
371
|
+
dataRef.current = data;
|
|
372
|
+
etagRef.current = etag;
|
|
373
|
+
}, [data, etag]);
|
|
374
|
+
const handleSuccess = useEventCallback(onSuccess);
|
|
375
|
+
const handleError = useEventCallback(onError);
|
|
376
|
+
const fetchMetadata = useCallback3(async () => {
|
|
331
377
|
if (!enabled) return;
|
|
332
378
|
try {
|
|
333
379
|
setIsLoading(true);
|
|
@@ -335,7 +381,7 @@ function useObject(objectName, options = {}) {
|
|
|
335
381
|
setFromCache(false);
|
|
336
382
|
if (useCache) {
|
|
337
383
|
const result = await client.meta.getCached(objectName, {
|
|
338
|
-
ifNoneMatch: ifNoneMatch ||
|
|
384
|
+
ifNoneMatch: ifNoneMatch || etagRef.current,
|
|
339
385
|
ifModifiedSince
|
|
340
386
|
});
|
|
341
387
|
if (result.notModified) {
|
|
@@ -346,24 +392,24 @@ function useObject(objectName, options = {}) {
|
|
|
346
392
|
setEtag(result.etag.value);
|
|
347
393
|
}
|
|
348
394
|
}
|
|
349
|
-
|
|
395
|
+
handleSuccess(result.data || dataRef.current);
|
|
350
396
|
} else {
|
|
351
397
|
const result = await client.meta.getItem("object", objectName);
|
|
352
398
|
setData(result);
|
|
353
|
-
|
|
399
|
+
handleSuccess(result);
|
|
354
400
|
}
|
|
355
401
|
} catch (err) {
|
|
356
402
|
const error2 = err instanceof Error ? err : new Error("Failed to fetch object metadata");
|
|
357
403
|
setError(error2);
|
|
358
|
-
|
|
404
|
+
handleError(error2);
|
|
359
405
|
} finally {
|
|
360
406
|
setIsLoading(false);
|
|
361
407
|
}
|
|
362
|
-
}, [client, objectName, locale, enabled, useCache, ifNoneMatch, ifModifiedSince,
|
|
363
|
-
|
|
408
|
+
}, [client, objectName, locale, enabled, useCache, ifNoneMatch, ifModifiedSince, handleSuccess, handleError]);
|
|
409
|
+
useEffect3(() => {
|
|
364
410
|
fetchMetadata();
|
|
365
411
|
}, [fetchMetadata]);
|
|
366
|
-
const refetch =
|
|
412
|
+
const refetch = useCallback3(async () => {
|
|
367
413
|
await fetchMetadata();
|
|
368
414
|
}, [fetchMetadata]);
|
|
369
415
|
return {
|
|
@@ -382,26 +428,28 @@ function useView(objectName, viewType = "list", options = {}) {
|
|
|
382
428
|
const [isLoading, setIsLoading] = useState2(true);
|
|
383
429
|
const [error, setError] = useState2(null);
|
|
384
430
|
const { enabled = true, onSuccess, onError } = options;
|
|
385
|
-
const
|
|
431
|
+
const handleSuccess = useEventCallback(onSuccess);
|
|
432
|
+
const handleError = useEventCallback(onError);
|
|
433
|
+
const fetchView = useCallback3(async () => {
|
|
386
434
|
if (!enabled) return;
|
|
387
435
|
try {
|
|
388
436
|
setIsLoading(true);
|
|
389
437
|
setError(null);
|
|
390
438
|
const result = await client.meta.getView(objectName, viewType);
|
|
391
439
|
setData(result);
|
|
392
|
-
|
|
440
|
+
handleSuccess(result);
|
|
393
441
|
} catch (err) {
|
|
394
442
|
const error2 = err instanceof Error ? err : new Error("Failed to fetch view configuration");
|
|
395
443
|
setError(error2);
|
|
396
|
-
|
|
444
|
+
handleError(error2);
|
|
397
445
|
} finally {
|
|
398
446
|
setIsLoading(false);
|
|
399
447
|
}
|
|
400
|
-
}, [client, objectName, viewType, locale, enabled,
|
|
401
|
-
|
|
448
|
+
}, [client, objectName, viewType, locale, enabled, handleSuccess, handleError]);
|
|
449
|
+
useEffect3(() => {
|
|
402
450
|
fetchView();
|
|
403
451
|
}, [fetchView]);
|
|
404
|
-
const refetch =
|
|
452
|
+
const refetch = useCallback3(async () => {
|
|
405
453
|
await fetchView();
|
|
406
454
|
}, [fetchView]);
|
|
407
455
|
return {
|
|
@@ -430,26 +478,29 @@ function useMetadata(fetcher, options = {}) {
|
|
|
430
478
|
const [isLoading, setIsLoading] = useState2(true);
|
|
431
479
|
const [error, setError] = useState2(null);
|
|
432
480
|
const { enabled = true, onSuccess, onError } = options;
|
|
433
|
-
const
|
|
481
|
+
const runFetcher = useEventCallback(fetcher);
|
|
482
|
+
const handleSuccess = useEventCallback(onSuccess);
|
|
483
|
+
const handleError = useEventCallback(onError);
|
|
484
|
+
const fetchMetadata = useCallback3(async () => {
|
|
434
485
|
if (!enabled) return;
|
|
435
486
|
try {
|
|
436
487
|
setIsLoading(true);
|
|
437
488
|
setError(null);
|
|
438
|
-
const result = await
|
|
489
|
+
const result = await runFetcher(client);
|
|
439
490
|
setData(result);
|
|
440
|
-
|
|
491
|
+
handleSuccess(result);
|
|
441
492
|
} catch (err) {
|
|
442
493
|
const error2 = err instanceof Error ? err : new Error("Failed to fetch metadata");
|
|
443
494
|
setError(error2);
|
|
444
|
-
|
|
495
|
+
handleError(error2);
|
|
445
496
|
} finally {
|
|
446
497
|
setIsLoading(false);
|
|
447
498
|
}
|
|
448
|
-
}, [client,
|
|
449
|
-
|
|
499
|
+
}, [client, runFetcher, locale, enabled, handleSuccess, handleError]);
|
|
500
|
+
useEffect3(() => {
|
|
450
501
|
fetchMetadata();
|
|
451
502
|
}, [fetchMetadata]);
|
|
452
|
-
const refetch =
|
|
503
|
+
const refetch = useCallback3(async () => {
|
|
453
504
|
await fetchMetadata();
|
|
454
505
|
}, [fetchMetadata]);
|
|
455
506
|
return {
|
|
@@ -462,11 +513,11 @@ function useMetadata(fetcher, options = {}) {
|
|
|
462
513
|
}
|
|
463
514
|
|
|
464
515
|
// src/realtime-hooks.tsx
|
|
465
|
-
import { useEffect as
|
|
516
|
+
import { useEffect as useEffect4, useState as useState3 } from "react";
|
|
466
517
|
function useMetadataSubscription(type, options) {
|
|
467
518
|
const client = useClient();
|
|
468
519
|
const [event, setEvent] = useState3(null);
|
|
469
|
-
|
|
520
|
+
useEffect4(() => {
|
|
470
521
|
if (!client) return;
|
|
471
522
|
const unsubscribe = client.events.subscribeMetadata(
|
|
472
523
|
type,
|
|
@@ -482,7 +533,7 @@ function useMetadataSubscription(type, options) {
|
|
|
482
533
|
function useDataSubscription(object, options) {
|
|
483
534
|
const client = useClient();
|
|
484
535
|
const [event, setEvent] = useState3(null);
|
|
485
|
-
|
|
536
|
+
useEffect4(() => {
|
|
486
537
|
if (!client) return;
|
|
487
538
|
const unsubscribe = client.events.subscribeData(
|
|
488
539
|
object,
|
|
@@ -497,36 +548,61 @@ function useDataSubscription(object, options) {
|
|
|
497
548
|
}
|
|
498
549
|
function useMetadataSubscriptionCallback(type, callback, options) {
|
|
499
550
|
const client = useClient();
|
|
500
|
-
|
|
551
|
+
const handleEvent = useEventCallback(callback);
|
|
552
|
+
useEffect4(() => {
|
|
501
553
|
if (!client) return;
|
|
502
554
|
const unsubscribe = client.events.subscribeMetadata(
|
|
503
555
|
type,
|
|
504
|
-
|
|
556
|
+
handleEvent,
|
|
505
557
|
options
|
|
506
558
|
);
|
|
507
559
|
return () => {
|
|
508
560
|
unsubscribe();
|
|
509
561
|
};
|
|
510
|
-
}, [client, type,
|
|
562
|
+
}, [client, type, handleEvent, options?.packageId]);
|
|
511
563
|
}
|
|
512
564
|
function useDataSubscriptionCallback(object, callback, options) {
|
|
513
565
|
const client = useClient();
|
|
514
|
-
|
|
566
|
+
const handleEvent = useEventCallback(callback);
|
|
567
|
+
useEffect4(() => {
|
|
515
568
|
if (!client) return;
|
|
516
569
|
const unsubscribe = client.events.subscribeData(
|
|
517
570
|
object,
|
|
518
|
-
|
|
571
|
+
handleEvent,
|
|
519
572
|
options
|
|
520
573
|
);
|
|
521
574
|
return () => {
|
|
522
575
|
unsubscribe();
|
|
523
576
|
};
|
|
524
|
-
}, [client, object,
|
|
577
|
+
}, [client, object, handleEvent, options?.recordId]);
|
|
578
|
+
}
|
|
579
|
+
function useBulkDataSubscription(object) {
|
|
580
|
+
const client = useClient();
|
|
581
|
+
const [event, setEvent] = useState3(null);
|
|
582
|
+
useEffect4(() => {
|
|
583
|
+
if (!client) return;
|
|
584
|
+
const unsubscribe = client.events.subscribeBulkData(object, (e) => setEvent(e));
|
|
585
|
+
return () => {
|
|
586
|
+
unsubscribe();
|
|
587
|
+
};
|
|
588
|
+
}, [client, object]);
|
|
589
|
+
return event;
|
|
590
|
+
}
|
|
591
|
+
function useBulkDataSubscriptionCallback(object, callback) {
|
|
592
|
+
const client = useClient();
|
|
593
|
+
const handleEvent = useEventCallback(callback);
|
|
594
|
+
useEffect4(() => {
|
|
595
|
+
if (!client) return;
|
|
596
|
+
const unsubscribe = client.events.subscribeBulkData(object, handleEvent);
|
|
597
|
+
return () => {
|
|
598
|
+
unsubscribe();
|
|
599
|
+
};
|
|
600
|
+
}, [client, object, handleEvent]);
|
|
525
601
|
}
|
|
526
602
|
function useRealtimeConnection() {
|
|
527
603
|
const client = useClient();
|
|
528
604
|
const [connected, setConnected] = useState3(true);
|
|
529
|
-
|
|
605
|
+
useEffect4(() => {
|
|
530
606
|
if (!client) {
|
|
531
607
|
setConnected(false);
|
|
532
608
|
return;
|
|
@@ -536,10 +612,8 @@ function useRealtimeConnection() {
|
|
|
536
612
|
return connected;
|
|
537
613
|
}
|
|
538
614
|
function useAutoRefresh(object, refetch, options) {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}, [refetch]);
|
|
542
|
-
useDataSubscriptionCallback(object, handleEvent, options);
|
|
615
|
+
useDataSubscriptionCallback(object, (_event) => refetch(), options);
|
|
616
|
+
useBulkDataSubscriptionCallback(object, (_event) => refetch());
|
|
543
617
|
}
|
|
544
618
|
|
|
545
619
|
// src/index.tsx
|
|
@@ -550,6 +624,8 @@ export {
|
|
|
550
624
|
ObjectStackLocaleContext,
|
|
551
625
|
ObjectStackProvider,
|
|
552
626
|
useAutoRefresh,
|
|
627
|
+
useBulkDataSubscription,
|
|
628
|
+
useBulkDataSubscriptionCallback,
|
|
553
629
|
useClient,
|
|
554
630
|
useDataSubscription,
|
|
555
631
|
useDataSubscriptionCallback,
|