reactjrx 1.54.0 → 1.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -35,12 +35,17 @@ function primitiveEqual(objA, objB) {
35
35
  return false;
36
36
  }
37
37
  function useObserve(source$, unsafeOptions, unsafeDeps) {
38
- const options = unsafeOptions != null && !Array.isArray(unsafeOptions) ? unsafeOptions : { defaultValue: void 0, key: "" };
38
+ const options = unsafeOptions != null && !Array.isArray(unsafeOptions) ? unsafeOptions : {
39
+ defaultValue: void 0,
40
+ key: "",
41
+ unsubscribeOnUnmount: true
42
+ };
39
43
  const deps = unsafeDeps == null && Array.isArray(unsafeOptions) ? unsafeOptions : typeof source$ === "function" ? unsafeDeps ?? [] : [source$];
40
44
  const valueRef = react.useRef(
41
45
  "getValue" in source$ && typeof source$.getValue === "function" ? source$.getValue() : options.defaultValue
42
46
  );
43
47
  const sourceRef = useLiveRef(source$);
48
+ const optionsRef = useLiveRef(options);
44
49
  const subscribe = react.useCallback(
45
50
  (next) => {
46
51
  const source = sourceRef.current;
@@ -64,6 +69,8 @@ function useObserve(source$, unsafeOptions, unsafeDeps) {
64
69
  })
65
70
  ).subscribe(next);
66
71
  return () => {
72
+ if (optionsRef.current.unsubscribeOnUnmount === false)
73
+ return;
67
74
  sub.unsubscribe();
68
75
  };
69
76
  },
@@ -391,175 +398,6 @@ function retryBackoff(config) {
391
398
  );
392
399
  });
393
400
  }
394
- function shallowEqual(objA, objB) {
395
- if (objA === null || objA === void 0 || objB === void 0) {
396
- return objA === objB;
397
- }
398
- if (typeof objA !== "object" || typeof objB !== "object") {
399
- return objA === objB;
400
- }
401
- if (objA.constructor !== (objB == null ? void 0 : objB.constructor)) {
402
- return false;
403
- }
404
- const keysA = Object.keys(objA);
405
- const keysB = Object.keys(objB);
406
- if (keysA.length !== keysB.length) {
407
- return false;
408
- }
409
- for (const key of keysA) {
410
- if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
411
- return false;
412
- }
413
- }
414
- return true;
415
- }
416
- const retryOnError = (options) => retryBackoff({
417
- initialInterval: 100,
418
- ...typeof options.retry === "function" ? {
419
- shouldRetry: options.retry
420
- } : {
421
- maxRetries: options.retry === false ? 0 : options.retry ?? 3
422
- }
423
- });
424
- const mergeResults = (stream$) => stream$.pipe(
425
- rxjs.scan(
426
- (acc, current) => {
427
- return {
428
- ...acc,
429
- ...current
430
- };
431
- },
432
- {
433
- data: void 0,
434
- error: void 0,
435
- fetchStatus: "idle",
436
- status: "loading"
437
- }
438
- ),
439
- rxjs.distinctUntilChanged(
440
- ({ data: prevData, ...prev }, { data: currData, ...curr }) => shallowEqual(prev, curr) && shallowEqual(prevData, currData)
441
- )
442
- );
443
- function useAsyncQuery(query, mapOperatorOrOptions, options = {}) {
444
- const queryRef = useLiveRef(query);
445
- const triggerSubject = useSubject();
446
- const resetSubject = useSubject({
447
- /**
448
- * @important
449
- * Because async query can still run after unmount, the user might
450
- * want to use reset for whatever reason. We will only manually complete
451
- * this subject whenever the main query hook finalize.
452
- */
453
- completeOnUnmount: false
454
- });
455
- const optionsRef = useLiveRef(
456
- typeof mapOperatorOrOptions === "object" ? mapOperatorOrOptions : options
457
- );
458
- const data$ = useBehaviorSubject({
459
- data: void 0,
460
- error: void 0,
461
- status: "idle"
462
- });
463
- const mapOperator = typeof mapOperatorOrOptions === "string" ? mapOperatorOrOptions : "merge";
464
- react.useEffect(() => {
465
- const switchOperator = mapOperator === "concat" ? rxjs.concatMap : mapOperator === "switch" ? rxjs.switchMap : rxjs.mergeMap;
466
- const subscription = rxjs.merge(
467
- resetSubject.current.pipe(
468
- rxjs.map(
469
- () => ({
470
- status: "idle",
471
- data: void 0,
472
- error: void 0
473
- })
474
- )
475
- ),
476
- triggerSubject.current.pipe(
477
- switchOperator((args) => {
478
- const isLastMutationCalled = triggerSubject.current.pipe(
479
- rxjs.take(1),
480
- rxjs.map(() => mapOperator === "concat"),
481
- rxjs.startWith(true)
482
- );
483
- return rxjs.merge(
484
- rxjs.of({
485
- status: "loading"
486
- }),
487
- rxjs.combineLatest([
488
- rxjs.defer(() => rxjs.from(queryRef.current(args))).pipe(
489
- retryOnError(optionsRef.current),
490
- rxjs.first(),
491
- rxjs.map((data) => ({ data, isError: false })),
492
- rxjs.catchError((error) => {
493
- console.error(error);
494
- if (optionsRef.current.onError != null) {
495
- optionsRef.current.onError(error, args);
496
- }
497
- return rxjs.of({ data: error, isError: true });
498
- })
499
- ),
500
- isLastMutationCalled
501
- ]).pipe(
502
- rxjs.map(([{ data, isError }, isLastMutationCalled2]) => {
503
- if (!isError) {
504
- if (optionsRef.current.onSuccess != null)
505
- optionsRef.current.onSuccess(data, args);
506
- }
507
- if (isLastMutationCalled2) {
508
- return isError ? {
509
- status: "error",
510
- error: data,
511
- data: void 0
512
- } : {
513
- status: "success",
514
- error: void 0,
515
- data
516
- };
517
- }
518
- return void 0;
519
- }),
520
- rxjs.takeUntil(resetSubject.current)
521
- )
522
- );
523
- }),
524
- optionsRef.current.triggerHook ?? rxjs.identity,
525
- rxjs.finalize(() => {
526
- resetSubject.current.complete();
527
- })
528
- )
529
- ).pipe(
530
- rxjs.filter((state) => !!state && !!Object.keys(state).length),
531
- /**
532
- * @important
533
- * state update optimization
534
- */
535
- rxjs.distinctUntilChanged(shallowEqual)
536
- ).subscribe((state) => {
537
- data$.current.next({
538
- ...data$.current.getValue(),
539
- ...state
540
- });
541
- });
542
- return () => {
543
- if (optionsRef.current.cancelOnUnMount) {
544
- subscription.unsubscribe();
545
- }
546
- };
547
- }, [mapOperator]);
548
- const result = useObserve(
549
- () => data$.current,
550
- {
551
- defaultValue: data$.current.getValue()
552
- },
553
- []
554
- );
555
- const mutate = react.useCallback((arg) => {
556
- triggerSubject.current.next(arg);
557
- }, []);
558
- const reset = react.useCallback(() => {
559
- resetSubject.current.next();
560
- }, []);
561
- return { ...result, isLoading: result.status === "loading", mutate, reset };
562
- }
563
401
  const Context = react.createContext({
564
402
  client: null
565
403
  });
@@ -574,7 +412,7 @@ const ClientEffect = ({
574
412
  }, [client]);
575
413
  return null;
576
414
  };
577
- const Provider = react.memo(
415
+ const QueryClientProvider = react.memo(
578
416
  ({ children, client }) => {
579
417
  const value = react.useMemo(() => ({ client: client.client }), [client]);
580
418
  return /* @__PURE__ */ jsxRuntime.jsxs(Context.Provider, { value, children: [
@@ -590,7 +428,92 @@ const useQueryClient = () => {
590
428
  }
591
429
  return context.client;
592
430
  };
431
+ function useMutation(options) {
432
+ const client = useQueryClient();
433
+ const optionsRef = useLiveRef(options);
434
+ const optionsSubject = useBehaviorSubject(options);
435
+ const [bufferTriggers, setBufferTriggers] = react.useState([]);
436
+ const createMutation = react.useCallback(
437
+ () => client.createMutation(
438
+ optionsSubject.current.pipe(
439
+ rxjs.map((options2) => ({
440
+ mutationKey: ["none"],
441
+ ...options2
442
+ }))
443
+ )
444
+ ),
445
+ []
446
+ );
447
+ const [mutation, setMutation] = react.useState(void 0);
448
+ const { mutation$, trigger$, reset$ } = mutation ?? {};
449
+ react.useEffect(() => {
450
+ const mutation2 = createMutation();
451
+ setMutation(mutation2);
452
+ return () => {
453
+ mutation2.destroy();
454
+ };
455
+ }, []);
456
+ const result = useObserve(
457
+ () => !mutation$ ? rxjs.NEVER : mutation$.pipe(
458
+ optionsRef.current.__triggerHook ?? rxjs.identity,
459
+ rxjs.finalize(() => {
460
+ console.log("finalize");
461
+ })
462
+ ),
463
+ {
464
+ defaultValue: {
465
+ data: void 0,
466
+ error: void 0,
467
+ status: "idle"
468
+ },
469
+ unsubscribeOnUnmount: optionsRef.current.cancelOnUnMount ?? false
470
+ },
471
+ [mutation$]
472
+ );
473
+ const mutate = react.useCallback(
474
+ (mutationArgs) => {
475
+ if (!trigger$) {
476
+ setBufferTriggers((value) => [...value, mutationArgs]);
477
+ }
478
+ trigger$ == null ? void 0 : trigger$.next(mutationArgs);
479
+ },
480
+ [trigger$]
481
+ );
482
+ react.useEffect(() => {
483
+ if (mutation && !mutation.getClosed()) {
484
+ bufferTriggers.forEach((args) => {
485
+ mutation.trigger$.next(args);
486
+ });
487
+ }
488
+ }, [bufferTriggers, mutation]);
489
+ const reset = react.useCallback(() => {
490
+ reset$ == null ? void 0 : reset$.next();
491
+ }, [reset$]);
492
+ return { mutate, reset, ...result };
493
+ }
593
494
  const arrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
495
+ function shallowEqual(objA, objB) {
496
+ if (objA === null || objA === void 0 || objB === void 0) {
497
+ return objA === objB;
498
+ }
499
+ if (typeof objA !== "object" || typeof objB !== "object") {
500
+ return objA === objB;
501
+ }
502
+ if (objA.constructor !== (objB == null ? void 0 : objB.constructor)) {
503
+ return false;
504
+ }
505
+ const keysA = Object.keys(objA);
506
+ const keysB = Object.keys(objB);
507
+ if (keysA.length !== keysB.length) {
508
+ return false;
509
+ }
510
+ for (const key of keysA) {
511
+ if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
512
+ return false;
513
+ }
514
+ }
515
+ return true;
516
+ }
594
517
  function isDefined(arg) {
595
518
  return arg !== null && arg !== void 0;
596
519
  }
@@ -775,6 +698,33 @@ const serializeKey = (key) => {
775
698
  return "[]";
776
699
  return serializeObject(key);
777
700
  };
701
+ const retryOnError = (options) => retryBackoff({
702
+ initialInterval: 100,
703
+ ...typeof options.retry === "function" ? {
704
+ shouldRetry: options.retry
705
+ } : {
706
+ maxRetries: options.retry === false ? 0 : options.retry ?? 3
707
+ }
708
+ });
709
+ const mergeResults$1 = (stream$) => stream$.pipe(
710
+ rxjs.scan(
711
+ (acc, current) => {
712
+ return {
713
+ ...acc,
714
+ ...current
715
+ };
716
+ },
717
+ {
718
+ data: void 0,
719
+ error: void 0,
720
+ fetchStatus: "idle",
721
+ status: "loading"
722
+ }
723
+ ),
724
+ rxjs.distinctUntilChanged(
725
+ ({ data: prevData, ...prev }, { data: currData, ...curr }) => shallowEqual(prev, curr) && shallowEqual(prevData, currData)
726
+ )
727
+ );
778
728
  const resetStyle = { backgroundColor: "transparent", color: "inherit" };
779
729
  function createLogger(env) {
780
730
  const _logger = {
@@ -1529,11 +1479,139 @@ const dispatchExternalRefetchToAllQueries = ({
1529
1479
  }),
1530
1480
  rxjs.filter((trigger2) => trigger2.type !== "refetch")
1531
1481
  );
1482
+ const mergeResults = (stream$) => stream$.pipe(
1483
+ rxjs.scan(
1484
+ (acc, current) => {
1485
+ return {
1486
+ ...acc,
1487
+ ...current
1488
+ };
1489
+ },
1490
+ {
1491
+ data: void 0,
1492
+ error: void 0,
1493
+ // fetchStatus: "idle",
1494
+ status: "loading"
1495
+ }
1496
+ ),
1497
+ rxjs.distinctUntilChanged(
1498
+ ({ data: prevData, ...prev }, { data: currData, ...curr }) => shallowEqual(prev, curr) && shallowEqual(prevData, currData)
1499
+ )
1500
+ );
1501
+ class MutationClient {
1502
+ constructor() {
1503
+ __publicField(this, "createMutation", (options$) => {
1504
+ const trigger$ = new rxjs.Subject();
1505
+ const reset$ = new rxjs.Subject();
1506
+ let closed = false;
1507
+ const destroy = () => {
1508
+ if (closed) {
1509
+ throw new Error("Trying to close an already closed mutation");
1510
+ }
1511
+ closed = true;
1512
+ trigger$.complete();
1513
+ reset$.complete();
1514
+ };
1515
+ const initOptions = options$.pipe(
1516
+ rxjs.tap(({ mutationKey }) => {
1517
+ const serializedKey = serializeKey(mutationKey);
1518
+ Logger.log("query$", serializedKey);
1519
+ }),
1520
+ rxjs.take(1)
1521
+ );
1522
+ const mutation$ = initOptions.pipe(
1523
+ rxjs.mergeMap(
1524
+ (options) => rxjs.of(options).pipe(
1525
+ options.__queryInitHook ?? rxjs.identity
1526
+ )
1527
+ ),
1528
+ rxjs.mergeMap((options) => {
1529
+ const { mapOperator } = options;
1530
+ const switchOperator = mapOperator === "concat" ? rxjs.concatMap : mapOperator === "switch" ? rxjs.switchMap : rxjs.mergeMap;
1531
+ return trigger$.pipe(
1532
+ rxjs.withLatestFrom(options$),
1533
+ switchOperator(([mutationArg, { mutationFn }]) => {
1534
+ const queryRunner$ = rxjs.defer(
1535
+ () => rxjs.from(mutationFn(mutationArg))
1536
+ ).pipe(
1537
+ retryOnError(options),
1538
+ rxjs.take(1),
1539
+ rxjs.map((data) => ({ data, isError: false })),
1540
+ rxjs.catchError((error) => {
1541
+ console.error(error);
1542
+ if (options.onError != null) {
1543
+ options.onError(error, mutationArg);
1544
+ }
1545
+ return rxjs.of({ data: error, isError: true });
1546
+ }),
1547
+ rxjs.share()
1548
+ );
1549
+ const queryIsOver$ = queryRunner$.pipe(
1550
+ rxjs.map(({ data, isError }) => isError || data)
1551
+ );
1552
+ const isThisCurrentFunctionLastOneCalled = trigger$.pipe(
1553
+ rxjs.take(1),
1554
+ rxjs.map(() => mapOperator === "concat"),
1555
+ rxjs.startWith(true),
1556
+ rxjs.takeUntil(queryIsOver$)
1557
+ );
1558
+ const loading$ = rxjs.of({
1559
+ status: "loading"
1560
+ });
1561
+ return rxjs.merge(
1562
+ loading$,
1563
+ rxjs.combineLatest([
1564
+ queryRunner$,
1565
+ isThisCurrentFunctionLastOneCalled
1566
+ ]).pipe(
1567
+ rxjs.map(([{ data, isError }, isLastMutationCalled]) => {
1568
+ if (!isError) {
1569
+ if (options.onSuccess != null)
1570
+ options.onSuccess(data, mutationArg);
1571
+ }
1572
+ if (isLastMutationCalled) {
1573
+ return isError ? {
1574
+ status: "error",
1575
+ error: data,
1576
+ data: void 0
1577
+ } : {
1578
+ status: "success",
1579
+ error: void 0,
1580
+ data
1581
+ };
1582
+ }
1583
+ return {};
1584
+ }),
1585
+ rxjs.takeUntil(reset$)
1586
+ )
1587
+ ).pipe(options.__queryRunnerHook ?? rxjs.identity);
1588
+ }),
1589
+ options.__queryTriggerHook ?? rxjs.identity,
1590
+ mergeResults
1591
+ );
1592
+ }),
1593
+ rxjs.finalize(() => {
1594
+ if (!closed) {
1595
+ destroy();
1596
+ }
1597
+ })
1598
+ );
1599
+ return {
1600
+ mutation$,
1601
+ trigger$,
1602
+ reset$,
1603
+ destroy,
1604
+ getClosed: () => closed
1605
+ };
1606
+ });
1607
+ }
1608
+ }
1532
1609
  const createClient = () => {
1533
1610
  const queryStore = createQueryStore();
1534
1611
  const invalidationClient = createInvalidationClient({ queryStore });
1535
1612
  const cacheClient = createCacheClient({ queryStore });
1536
1613
  const refetchClient = createRefetchClient();
1614
+ const mutationClient = new MutationClient();
1537
1615
  let hasCalledStart = false;
1538
1616
  const query = ({
1539
1617
  key,
@@ -1610,7 +1688,7 @@ const createClient = () => {
1610
1688
  trigger$
1611
1689
  })
1612
1690
  ),
1613
- mergeResults,
1691
+ mergeResults$1,
1614
1692
  rxjs.withLatestFrom(options$),
1615
1693
  rxjs.takeWhile(([result, options]) => {
1616
1694
  const shouldStop = result.data !== void 0 && options.terminateOnFirstResult;
@@ -1659,6 +1737,7 @@ const createClient = () => {
1659
1737
  start,
1660
1738
  query,
1661
1739
  queryStore,
1740
+ ...mutationClient,
1662
1741
  ...invalidationClient,
1663
1742
  ...cacheClient,
1664
1743
  ...refetchClient
@@ -1671,7 +1750,7 @@ class QueryClient {
1671
1750
  }
1672
1751
  }
1673
1752
  exports.QueryClient = QueryClient;
1674
- exports.QueryClientProvider = Provider;
1753
+ exports.QueryClientProvider = QueryClientProvider;
1675
1754
  exports.SIGNAL_RESET = SIGNAL_RESET;
1676
1755
  exports.createClient = createClient;
1677
1756
  exports.createLocalforageAdapter = createLocalforageAdapter;
@@ -1681,9 +1760,9 @@ exports.getDelay = getDelay;
1681
1760
  exports.retryBackoff = retryBackoff;
1682
1761
  exports.signal = signal;
1683
1762
  exports.trigger = trigger;
1684
- exports.useAsyncQuery = useAsyncQuery;
1685
1763
  exports.useBehaviorSubject = useBehaviorSubject;
1686
1764
  exports.useLiveRef = useLiveRef;
1765
+ exports.useMutation = useMutation;
1687
1766
  exports.useObserve = useObserve;
1688
1767
  exports.useObserveCallback = useObserveCallback;
1689
1768
  exports.usePersistSignals = usePersistSignals;
package/dist/index.d.ts CHANGED
@@ -17,4 +17,4 @@ export * from "./lib/queries/react/mutations/useMutation";
17
17
  export * from "./lib/queries/react/queries/useQuery";
18
18
  export * from "./lib/queries/react/useSubscribeEffect";
19
19
  export * from "./lib/queries/client/createClient";
20
- export { Provider as QueryClientProvider, useQueryClient } from "./lib/queries/react/Provider";
20
+ export { QueryClientProvider, useQueryClient } from "./lib/queries/react/Provider";
package/dist/index.js CHANGED
@@ -4,8 +4,8 @@ var __publicField = (obj, key, value) => {
4
4
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  return value;
6
6
  };
7
- import { useRef, useMemo, useCallback, useSyncExternalStore, useEffect, createContext, memo, useContext } from "react";
8
- import { distinctUntilChanged, tap, finalize, catchError, EMPTY, Subject, identity, BehaviorSubject, map, of, zip, merge, throttleTime, asyncScheduler, switchMap, from, defer, iif, timer, throwError, scan, take, startWith, combineLatest, first, takeUntil, filter, concatMap as concatMap$1, mergeMap, fromEvent, skip, withLatestFrom, retry, shareReplay, endWith, delay, share, pairwise, NEVER, takeWhile } from "rxjs";
7
+ import { useRef, useMemo, useCallback, useSyncExternalStore, useEffect, createContext, memo, useContext, useState } from "react";
8
+ import { distinctUntilChanged, tap, finalize, catchError, EMPTY, Subject, identity, BehaviorSubject, map, of, zip, merge, throttleTime, asyncScheduler, switchMap, from, defer, iif, timer, throwError, NEVER, fromEvent, filter, skip, withLatestFrom, scan, retry, shareReplay, endWith, delay, take, takeUntil, share, startWith, pairwise, mergeMap, combineLatest, concatMap as concatMap$1, takeWhile } from "rxjs";
9
9
  import { retryWhen, concatMap, tap as tap$1 } from "rxjs/operators";
10
10
  import { jsxs, jsx } from "react/jsx-runtime";
11
11
  const useLiveRef = (value) => {
@@ -33,12 +33,17 @@ function primitiveEqual(objA, objB) {
33
33
  return false;
34
34
  }
35
35
  function useObserve(source$, unsafeOptions, unsafeDeps) {
36
- const options = unsafeOptions != null && !Array.isArray(unsafeOptions) ? unsafeOptions : { defaultValue: void 0, key: "" };
36
+ const options = unsafeOptions != null && !Array.isArray(unsafeOptions) ? unsafeOptions : {
37
+ defaultValue: void 0,
38
+ key: "",
39
+ unsubscribeOnUnmount: true
40
+ };
37
41
  const deps = unsafeDeps == null && Array.isArray(unsafeOptions) ? unsafeOptions : typeof source$ === "function" ? unsafeDeps ?? [] : [source$];
38
42
  const valueRef = useRef(
39
43
  "getValue" in source$ && typeof source$.getValue === "function" ? source$.getValue() : options.defaultValue
40
44
  );
41
45
  const sourceRef = useLiveRef(source$);
46
+ const optionsRef = useLiveRef(options);
42
47
  const subscribe = useCallback(
43
48
  (next) => {
44
49
  const source = sourceRef.current;
@@ -62,6 +67,8 @@ function useObserve(source$, unsafeOptions, unsafeDeps) {
62
67
  })
63
68
  ).subscribe(next);
64
69
  return () => {
70
+ if (optionsRef.current.unsubscribeOnUnmount === false)
71
+ return;
65
72
  sub.unsubscribe();
66
73
  };
67
74
  },
@@ -389,175 +396,6 @@ function retryBackoff(config) {
389
396
  );
390
397
  });
391
398
  }
392
- function shallowEqual(objA, objB) {
393
- if (objA === null || objA === void 0 || objB === void 0) {
394
- return objA === objB;
395
- }
396
- if (typeof objA !== "object" || typeof objB !== "object") {
397
- return objA === objB;
398
- }
399
- if (objA.constructor !== (objB == null ? void 0 : objB.constructor)) {
400
- return false;
401
- }
402
- const keysA = Object.keys(objA);
403
- const keysB = Object.keys(objB);
404
- if (keysA.length !== keysB.length) {
405
- return false;
406
- }
407
- for (const key of keysA) {
408
- if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
409
- return false;
410
- }
411
- }
412
- return true;
413
- }
414
- const retryOnError = (options) => retryBackoff({
415
- initialInterval: 100,
416
- ...typeof options.retry === "function" ? {
417
- shouldRetry: options.retry
418
- } : {
419
- maxRetries: options.retry === false ? 0 : options.retry ?? 3
420
- }
421
- });
422
- const mergeResults = (stream$) => stream$.pipe(
423
- scan(
424
- (acc, current) => {
425
- return {
426
- ...acc,
427
- ...current
428
- };
429
- },
430
- {
431
- data: void 0,
432
- error: void 0,
433
- fetchStatus: "idle",
434
- status: "loading"
435
- }
436
- ),
437
- distinctUntilChanged(
438
- ({ data: prevData, ...prev }, { data: currData, ...curr }) => shallowEqual(prev, curr) && shallowEqual(prevData, currData)
439
- )
440
- );
441
- function useAsyncQuery(query, mapOperatorOrOptions, options = {}) {
442
- const queryRef = useLiveRef(query);
443
- const triggerSubject = useSubject();
444
- const resetSubject = useSubject({
445
- /**
446
- * @important
447
- * Because async query can still run after unmount, the user might
448
- * want to use reset for whatever reason. We will only manually complete
449
- * this subject whenever the main query hook finalize.
450
- */
451
- completeOnUnmount: false
452
- });
453
- const optionsRef = useLiveRef(
454
- typeof mapOperatorOrOptions === "object" ? mapOperatorOrOptions : options
455
- );
456
- const data$ = useBehaviorSubject({
457
- data: void 0,
458
- error: void 0,
459
- status: "idle"
460
- });
461
- const mapOperator = typeof mapOperatorOrOptions === "string" ? mapOperatorOrOptions : "merge";
462
- useEffect(() => {
463
- const switchOperator = mapOperator === "concat" ? concatMap$1 : mapOperator === "switch" ? switchMap : mergeMap;
464
- const subscription = merge(
465
- resetSubject.current.pipe(
466
- map(
467
- () => ({
468
- status: "idle",
469
- data: void 0,
470
- error: void 0
471
- })
472
- )
473
- ),
474
- triggerSubject.current.pipe(
475
- switchOperator((args) => {
476
- const isLastMutationCalled = triggerSubject.current.pipe(
477
- take(1),
478
- map(() => mapOperator === "concat"),
479
- startWith(true)
480
- );
481
- return merge(
482
- of({
483
- status: "loading"
484
- }),
485
- combineLatest([
486
- defer(() => from(queryRef.current(args))).pipe(
487
- retryOnError(optionsRef.current),
488
- first(),
489
- map((data) => ({ data, isError: false })),
490
- catchError((error) => {
491
- console.error(error);
492
- if (optionsRef.current.onError != null) {
493
- optionsRef.current.onError(error, args);
494
- }
495
- return of({ data: error, isError: true });
496
- })
497
- ),
498
- isLastMutationCalled
499
- ]).pipe(
500
- map(([{ data, isError }, isLastMutationCalled2]) => {
501
- if (!isError) {
502
- if (optionsRef.current.onSuccess != null)
503
- optionsRef.current.onSuccess(data, args);
504
- }
505
- if (isLastMutationCalled2) {
506
- return isError ? {
507
- status: "error",
508
- error: data,
509
- data: void 0
510
- } : {
511
- status: "success",
512
- error: void 0,
513
- data
514
- };
515
- }
516
- return void 0;
517
- }),
518
- takeUntil(resetSubject.current)
519
- )
520
- );
521
- }),
522
- optionsRef.current.triggerHook ?? identity,
523
- finalize(() => {
524
- resetSubject.current.complete();
525
- })
526
- )
527
- ).pipe(
528
- filter((state) => !!state && !!Object.keys(state).length),
529
- /**
530
- * @important
531
- * state update optimization
532
- */
533
- distinctUntilChanged(shallowEqual)
534
- ).subscribe((state) => {
535
- data$.current.next({
536
- ...data$.current.getValue(),
537
- ...state
538
- });
539
- });
540
- return () => {
541
- if (optionsRef.current.cancelOnUnMount) {
542
- subscription.unsubscribe();
543
- }
544
- };
545
- }, [mapOperator]);
546
- const result = useObserve(
547
- () => data$.current,
548
- {
549
- defaultValue: data$.current.getValue()
550
- },
551
- []
552
- );
553
- const mutate = useCallback((arg) => {
554
- triggerSubject.current.next(arg);
555
- }, []);
556
- const reset = useCallback(() => {
557
- resetSubject.current.next();
558
- }, []);
559
- return { ...result, isLoading: result.status === "loading", mutate, reset };
560
- }
561
399
  const Context = createContext({
562
400
  client: null
563
401
  });
@@ -572,7 +410,7 @@ const ClientEffect = ({
572
410
  }, [client]);
573
411
  return null;
574
412
  };
575
- const Provider = memo(
413
+ const QueryClientProvider = memo(
576
414
  ({ children, client }) => {
577
415
  const value = useMemo(() => ({ client: client.client }), [client]);
578
416
  return /* @__PURE__ */ jsxs(Context.Provider, { value, children: [
@@ -588,7 +426,92 @@ const useQueryClient = () => {
588
426
  }
589
427
  return context.client;
590
428
  };
429
+ function useMutation(options) {
430
+ const client = useQueryClient();
431
+ const optionsRef = useLiveRef(options);
432
+ const optionsSubject = useBehaviorSubject(options);
433
+ const [bufferTriggers, setBufferTriggers] = useState([]);
434
+ const createMutation = useCallback(
435
+ () => client.createMutation(
436
+ optionsSubject.current.pipe(
437
+ map((options2) => ({
438
+ mutationKey: ["none"],
439
+ ...options2
440
+ }))
441
+ )
442
+ ),
443
+ []
444
+ );
445
+ const [mutation, setMutation] = useState(void 0);
446
+ const { mutation$, trigger$, reset$ } = mutation ?? {};
447
+ useEffect(() => {
448
+ const mutation2 = createMutation();
449
+ setMutation(mutation2);
450
+ return () => {
451
+ mutation2.destroy();
452
+ };
453
+ }, []);
454
+ const result = useObserve(
455
+ () => !mutation$ ? NEVER : mutation$.pipe(
456
+ optionsRef.current.__triggerHook ?? identity,
457
+ finalize(() => {
458
+ console.log("finalize");
459
+ })
460
+ ),
461
+ {
462
+ defaultValue: {
463
+ data: void 0,
464
+ error: void 0,
465
+ status: "idle"
466
+ },
467
+ unsubscribeOnUnmount: optionsRef.current.cancelOnUnMount ?? false
468
+ },
469
+ [mutation$]
470
+ );
471
+ const mutate = useCallback(
472
+ (mutationArgs) => {
473
+ if (!trigger$) {
474
+ setBufferTriggers((value) => [...value, mutationArgs]);
475
+ }
476
+ trigger$ == null ? void 0 : trigger$.next(mutationArgs);
477
+ },
478
+ [trigger$]
479
+ );
480
+ useEffect(() => {
481
+ if (mutation && !mutation.getClosed()) {
482
+ bufferTriggers.forEach((args) => {
483
+ mutation.trigger$.next(args);
484
+ });
485
+ }
486
+ }, [bufferTriggers, mutation]);
487
+ const reset = useCallback(() => {
488
+ reset$ == null ? void 0 : reset$.next();
489
+ }, [reset$]);
490
+ return { mutate, reset, ...result };
491
+ }
591
492
  const arrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
493
+ function shallowEqual(objA, objB) {
494
+ if (objA === null || objA === void 0 || objB === void 0) {
495
+ return objA === objB;
496
+ }
497
+ if (typeof objA !== "object" || typeof objB !== "object") {
498
+ return objA === objB;
499
+ }
500
+ if (objA.constructor !== (objB == null ? void 0 : objB.constructor)) {
501
+ return false;
502
+ }
503
+ const keysA = Object.keys(objA);
504
+ const keysB = Object.keys(objB);
505
+ if (keysA.length !== keysB.length) {
506
+ return false;
507
+ }
508
+ for (const key of keysA) {
509
+ if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
510
+ return false;
511
+ }
512
+ }
513
+ return true;
514
+ }
592
515
  function isDefined(arg) {
593
516
  return arg !== null && arg !== void 0;
594
517
  }
@@ -773,6 +696,33 @@ const serializeKey = (key) => {
773
696
  return "[]";
774
697
  return serializeObject(key);
775
698
  };
699
+ const retryOnError = (options) => retryBackoff({
700
+ initialInterval: 100,
701
+ ...typeof options.retry === "function" ? {
702
+ shouldRetry: options.retry
703
+ } : {
704
+ maxRetries: options.retry === false ? 0 : options.retry ?? 3
705
+ }
706
+ });
707
+ const mergeResults$1 = (stream$) => stream$.pipe(
708
+ scan(
709
+ (acc, current) => {
710
+ return {
711
+ ...acc,
712
+ ...current
713
+ };
714
+ },
715
+ {
716
+ data: void 0,
717
+ error: void 0,
718
+ fetchStatus: "idle",
719
+ status: "loading"
720
+ }
721
+ ),
722
+ distinctUntilChanged(
723
+ ({ data: prevData, ...prev }, { data: currData, ...curr }) => shallowEqual(prev, curr) && shallowEqual(prevData, currData)
724
+ )
725
+ );
776
726
  const resetStyle = { backgroundColor: "transparent", color: "inherit" };
777
727
  function createLogger(env) {
778
728
  const _logger = {
@@ -1527,11 +1477,139 @@ const dispatchExternalRefetchToAllQueries = ({
1527
1477
  }),
1528
1478
  filter((trigger2) => trigger2.type !== "refetch")
1529
1479
  );
1480
+ const mergeResults = (stream$) => stream$.pipe(
1481
+ scan(
1482
+ (acc, current) => {
1483
+ return {
1484
+ ...acc,
1485
+ ...current
1486
+ };
1487
+ },
1488
+ {
1489
+ data: void 0,
1490
+ error: void 0,
1491
+ // fetchStatus: "idle",
1492
+ status: "loading"
1493
+ }
1494
+ ),
1495
+ distinctUntilChanged(
1496
+ ({ data: prevData, ...prev }, { data: currData, ...curr }) => shallowEqual(prev, curr) && shallowEqual(prevData, currData)
1497
+ )
1498
+ );
1499
+ class MutationClient {
1500
+ constructor() {
1501
+ __publicField(this, "createMutation", (options$) => {
1502
+ const trigger$ = new Subject();
1503
+ const reset$ = new Subject();
1504
+ let closed = false;
1505
+ const destroy = () => {
1506
+ if (closed) {
1507
+ throw new Error("Trying to close an already closed mutation");
1508
+ }
1509
+ closed = true;
1510
+ trigger$.complete();
1511
+ reset$.complete();
1512
+ };
1513
+ const initOptions = options$.pipe(
1514
+ tap(({ mutationKey }) => {
1515
+ const serializedKey = serializeKey(mutationKey);
1516
+ Logger.log("query$", serializedKey);
1517
+ }),
1518
+ take(1)
1519
+ );
1520
+ const mutation$ = initOptions.pipe(
1521
+ mergeMap(
1522
+ (options) => of(options).pipe(
1523
+ options.__queryInitHook ?? identity
1524
+ )
1525
+ ),
1526
+ mergeMap((options) => {
1527
+ const { mapOperator } = options;
1528
+ const switchOperator = mapOperator === "concat" ? concatMap$1 : mapOperator === "switch" ? switchMap : mergeMap;
1529
+ return trigger$.pipe(
1530
+ withLatestFrom(options$),
1531
+ switchOperator(([mutationArg, { mutationFn }]) => {
1532
+ const queryRunner$ = defer(
1533
+ () => from(mutationFn(mutationArg))
1534
+ ).pipe(
1535
+ retryOnError(options),
1536
+ take(1),
1537
+ map((data) => ({ data, isError: false })),
1538
+ catchError((error) => {
1539
+ console.error(error);
1540
+ if (options.onError != null) {
1541
+ options.onError(error, mutationArg);
1542
+ }
1543
+ return of({ data: error, isError: true });
1544
+ }),
1545
+ share()
1546
+ );
1547
+ const queryIsOver$ = queryRunner$.pipe(
1548
+ map(({ data, isError }) => isError || data)
1549
+ );
1550
+ const isThisCurrentFunctionLastOneCalled = trigger$.pipe(
1551
+ take(1),
1552
+ map(() => mapOperator === "concat"),
1553
+ startWith(true),
1554
+ takeUntil(queryIsOver$)
1555
+ );
1556
+ const loading$ = of({
1557
+ status: "loading"
1558
+ });
1559
+ return merge(
1560
+ loading$,
1561
+ combineLatest([
1562
+ queryRunner$,
1563
+ isThisCurrentFunctionLastOneCalled
1564
+ ]).pipe(
1565
+ map(([{ data, isError }, isLastMutationCalled]) => {
1566
+ if (!isError) {
1567
+ if (options.onSuccess != null)
1568
+ options.onSuccess(data, mutationArg);
1569
+ }
1570
+ if (isLastMutationCalled) {
1571
+ return isError ? {
1572
+ status: "error",
1573
+ error: data,
1574
+ data: void 0
1575
+ } : {
1576
+ status: "success",
1577
+ error: void 0,
1578
+ data
1579
+ };
1580
+ }
1581
+ return {};
1582
+ }),
1583
+ takeUntil(reset$)
1584
+ )
1585
+ ).pipe(options.__queryRunnerHook ?? identity);
1586
+ }),
1587
+ options.__queryTriggerHook ?? identity,
1588
+ mergeResults
1589
+ );
1590
+ }),
1591
+ finalize(() => {
1592
+ if (!closed) {
1593
+ destroy();
1594
+ }
1595
+ })
1596
+ );
1597
+ return {
1598
+ mutation$,
1599
+ trigger$,
1600
+ reset$,
1601
+ destroy,
1602
+ getClosed: () => closed
1603
+ };
1604
+ });
1605
+ }
1606
+ }
1530
1607
  const createClient = () => {
1531
1608
  const queryStore = createQueryStore();
1532
1609
  const invalidationClient = createInvalidationClient({ queryStore });
1533
1610
  const cacheClient = createCacheClient({ queryStore });
1534
1611
  const refetchClient = createRefetchClient();
1612
+ const mutationClient = new MutationClient();
1535
1613
  let hasCalledStart = false;
1536
1614
  const query = ({
1537
1615
  key,
@@ -1608,7 +1686,7 @@ const createClient = () => {
1608
1686
  trigger$
1609
1687
  })
1610
1688
  ),
1611
- mergeResults,
1689
+ mergeResults$1,
1612
1690
  withLatestFrom(options$),
1613
1691
  takeWhile(([result, options]) => {
1614
1692
  const shouldStop = result.data !== void 0 && options.terminateOnFirstResult;
@@ -1657,6 +1735,7 @@ const createClient = () => {
1657
1735
  start,
1658
1736
  query,
1659
1737
  queryStore,
1738
+ ...mutationClient,
1660
1739
  ...invalidationClient,
1661
1740
  ...cacheClient,
1662
1741
  ...refetchClient
@@ -1670,7 +1749,7 @@ class QueryClient {
1670
1749
  }
1671
1750
  export {
1672
1751
  QueryClient,
1673
- Provider as QueryClientProvider,
1752
+ QueryClientProvider,
1674
1753
  SIGNAL_RESET,
1675
1754
  createClient,
1676
1755
  createLocalforageAdapter,
@@ -1680,9 +1759,9 @@ export {
1680
1759
  retryBackoff,
1681
1760
  signal,
1682
1761
  trigger,
1683
- useAsyncQuery,
1684
1762
  useBehaviorSubject,
1685
1763
  useLiveRef,
1764
+ useMutation,
1686
1765
  useObserve,
1687
1766
  useObserveCallback,
1688
1767
  usePersistSignals,
@@ -3,6 +3,7 @@ import { type Observable, type BehaviorSubject } from "rxjs";
3
3
  interface Option<R = undefined> {
4
4
  defaultValue: R;
5
5
  key?: string;
6
+ unsubscribeOnUnmount?: boolean;
6
7
  }
7
8
  /**
8
9
  * @todo return first value if source is behavior subject
@@ -1,4 +1,4 @@
1
- import { type Observable, BehaviorSubject } from "rxjs";
1
+ import { type Observable, Subject, BehaviorSubject } from "rxjs";
2
2
  import { type QueryOptions, type QueryFn, type QueryTrigger, type QueryResult } from "./types";
3
3
  import { type QueryKey } from "./keys/types";
4
4
  export declare const createClient: () => {
@@ -38,15 +38,22 @@ export declare const createClient: () => {
38
38
  exact?: boolean | undefined;
39
39
  predicate?: ((storeObject: import("./store/createQueryStore").StoreObject<unknown>) => boolean) | undefined;
40
40
  }) => void;
41
+ createMutation: <T_4, MutationArg>(options$: Observable<import("./mutations/types").MutationOptions<T_4, MutationArg>>) => {
42
+ mutation$: Observable<import("./mutations/types").MutationResult<T_4>>;
43
+ trigger$: Subject<MutationArg>;
44
+ reset$: Subject<void>;
45
+ destroy: () => void;
46
+ getClosed: () => boolean;
47
+ };
41
48
  start: () => () => void;
42
- query: <T_4>({ key, fn$: maybeFn$, fn: maybeFn, trigger$: externalTrigger$, options$ }: {
49
+ query: <T_5>({ key, fn$: maybeFn$, fn: maybeFn, trigger$: externalTrigger$, options$ }: {
43
50
  key: QueryKey;
44
- fn?: QueryFn<T_4> | undefined;
45
- fn$?: Observable<QueryFn<T_4>> | undefined;
51
+ fn?: QueryFn<T_5> | undefined;
52
+ fn$?: Observable<QueryFn<T_5>> | undefined;
46
53
  trigger$?: Observable<QueryTrigger> | undefined;
47
- options$?: Observable<QueryOptions<T_4>> | undefined;
54
+ options$?: Observable<QueryOptions<T_5>> | undefined;
48
55
  }) => {
49
- result$: Observable<QueryResult<T_4>>;
56
+ result$: Observable<QueryResult<T_5>>;
50
57
  };
51
58
  queryStore: {
52
59
  set: (key: string, value: import("./store/createQueryStore").StoreObject<unknown>) => void;
@@ -0,0 +1,11 @@
1
+ import { type Observable, Subject } from "rxjs";
2
+ import { type MutationResult, type MutationOptions } from "./types";
3
+ export declare class MutationClient {
4
+ createMutation: <T, MutationArg>(options$: Observable<MutationOptions<T, MutationArg>>) => {
5
+ mutation$: Observable<MutationResult<T>>;
6
+ trigger$: Subject<MutationArg>;
7
+ reset$: Subject<void>;
8
+ destroy: () => void;
9
+ getClosed: () => boolean;
10
+ };
11
+ }
@@ -0,0 +1,3 @@
1
+ import { type Observable } from "rxjs";
2
+ import { type MutationResult } from "./types";
3
+ export declare const mergeResults: <T>(stream$: Observable<Partial<MutationResult<T>>>) => Observable<MutationResult<T>>;
@@ -0,0 +1,58 @@
1
+ import { type MonoTypeOperatorFunction, type Observable } from "rxjs";
2
+ import { type Query, type QueryResult } from "../types";
3
+ import { type QueryKey } from "../keys/types";
4
+ /**
5
+ * The default value `merge` is suitable for most use case.
6
+ * You should not have to worry too much about it and only consider changing
7
+ * it when specific need arise.
8
+ *
9
+ * `merge`:
10
+ * Run each async query as they are triggered without any cancellation or queue system.
11
+ * The result is always from the latest async query triggered, not necessarily
12
+ * the latest one running.
13
+ *
14
+ * `concat`:
15
+ * Unlike merge, it will trigger each async query sequentially following
16
+ * a queue system. The result is not necessarily the last triggered async query
17
+ * but the current running async query.
18
+ *
19
+ * `switch`:
20
+ * Only run the latest async query triggered and cancel any previously running one.
21
+ * Result correspond to the current running async query.
22
+ */
23
+ export type MapOperator = "switch" | "concat" | "merge";
24
+ export interface MutationResult<R> {
25
+ data: R | undefined;
26
+ status: "idle" | "loading" | "error" | "success";
27
+ error: unknown;
28
+ }
29
+ export type MutationFn<T, MutationArg> = ((arg: MutationArg) => Promise<T>) | ((arg: MutationArg) => Observable<T>);
30
+ export interface MutationOptions<Result, MutationArg> {
31
+ enabled?: boolean;
32
+ retry?: false | number | ((attempt: number, error: unknown) => boolean);
33
+ /**
34
+ * @important
35
+ * The hook with the lowest value will be taken into account
36
+ */
37
+ staleTime?: number;
38
+ /**
39
+ * Force the new query to be marked as stale. Only on first trigger
40
+ */
41
+ markStale?: boolean;
42
+ cacheTime?: number;
43
+ /**
44
+ * @important
45
+ * interval is paused until the query finish fetching. This avoid infinite
46
+ * loop of refetch
47
+ */
48
+ refetchInterval?: number | false | ((data: QueryResult<Result>["data"] | undefined, query: Query) => number | false);
49
+ terminateOnFirstResult?: boolean;
50
+ onError?: (error: unknown, arg: MutationArg) => void;
51
+ onSuccess?: (data: Result, arg: MutationArg) => void;
52
+ mutationFn: MutationFn<Result, MutationArg>;
53
+ mutationKey: QueryKey;
54
+ mapOperator?: MapOperator;
55
+ __queryInitHook?: MonoTypeOperatorFunction<any>;
56
+ __queryRunnerHook?: MonoTypeOperatorFunction<any>;
57
+ __queryTriggerHook?: MonoTypeOperatorFunction<Partial<Result>>;
58
+ }
@@ -3,7 +3,7 @@ import { type QueryClient, type createClient } from "../client/createClient";
3
3
  export declare const Context: import("react").Context<{
4
4
  client: ReturnType<typeof createClient>;
5
5
  }>;
6
- export declare const Provider: import("react").MemoExoticComponent<({ children, client }: {
6
+ export declare const QueryClientProvider: import("react").MemoExoticComponent<({ children, client }: {
7
7
  children: ReactNode;
8
8
  client: QueryClient;
9
9
  }) => import("react/jsx-runtime").JSX.Element>;
@@ -44,15 +44,22 @@ export declare const useQueryClient: () => {
44
44
  exact?: boolean | undefined;
45
45
  predicate?: ((storeObject: import("../client/store/createQueryStore").StoreObject<unknown>) => boolean) | undefined;
46
46
  }) => void;
47
+ createMutation: <T_4, MutationArg>(options$: import("rxjs").Observable<import("../client/mutations/types").MutationOptions<T_4, MutationArg>>) => {
48
+ mutation$: import("rxjs").Observable<import("../client/mutations/types").MutationResult<T_4>>;
49
+ trigger$: import("rxjs").Subject<MutationArg>;
50
+ reset$: import("rxjs").Subject<void>;
51
+ destroy: () => void;
52
+ getClosed: () => boolean;
53
+ };
47
54
  start: () => () => void;
48
- query: <T_4>({ key, fn$: maybeFn$, fn: maybeFn, trigger$: externalTrigger$, options$ }: {
55
+ query: <T_5>({ key, fn$: maybeFn$, fn: maybeFn, trigger$: externalTrigger$, options$ }: {
49
56
  key: import("../client/keys/types").QueryKey;
50
- fn?: import("../client/types").QueryFn<T_4> | undefined;
51
- fn$?: import("rxjs").Observable<import("../client/types").QueryFn<T_4>> | undefined;
57
+ fn?: import("../client/types").QueryFn<T_5> | undefined;
58
+ fn$?: import("rxjs").Observable<import("../client/types").QueryFn<T_5>> | undefined;
52
59
  trigger$?: import("rxjs").Observable<import("../client/types").QueryTrigger> | undefined;
53
- options$?: import("rxjs").Observable<import("../client/types").QueryOptions<T_4>> | undefined;
60
+ options$?: import("rxjs").Observable<import("../client/types").QueryOptions<T_5>> | undefined;
54
61
  }) => {
55
- result$: import("rxjs").Observable<import("../client/types").QueryResult<T_4>>;
62
+ result$: import("rxjs").Observable<import("../client/types").QueryResult<T_5>>;
56
63
  };
57
64
  queryStore: {
58
65
  set: (key: string, value: import("../client/store/createQueryStore").StoreObject<unknown>) => void;
@@ -1,23 +1,8 @@
1
- import { type MonoTypeOperatorFunction, type Observable } from "rxjs";
2
- interface QueryState<R> {
3
- data: R | undefined;
4
- status: "idle" | "loading" | "error" | "success";
5
- error: unknown;
6
- }
7
- export interface AsyncQueryOptions<Result, Params> {
8
- retry?: false | number | ((attempt: number, error: unknown) => boolean);
9
- /**
10
- * Called for every async query on error.
11
- * `merge` mapping will run callback as they happen.
12
- * Use `concat` if you need to run callbacks in order of calling.
13
- */
14
- onError?: (error: unknown, params: Params) => void;
15
- /**
16
- * Called for every async query on success.
17
- * `merge` mapping will run callback as they happen.
18
- * Use `concat` if you need to run callbacks in order of calling.
19
- */
20
- onSuccess?: (data: Result, params: Params) => void;
1
+ import { type MonoTypeOperatorFunction } from "rxjs";
2
+ import { type MutationOptions } from "../../client/mutations/types";
3
+ import { type QueryKey } from "../../client/keys/types";
4
+ export type AsyncQueryOptions<Result, Params> = Omit<MutationOptions<Result, Params>, "mutationKey"> & {
5
+ mutationKey?: QueryKey;
21
6
  /**
22
7
  * When true, any running async query will be cancelled (when possible) on unmount.
23
8
  * You need to handle it yourself for promises if needed.
@@ -29,54 +14,33 @@ export interface AsyncQueryOptions<Result, Params> {
29
14
  * @default false
30
15
  */
31
16
  cancelOnUnMount?: boolean;
32
- /**
33
- * Only use for debugging.
34
- * It is not the main subscription hook, only the one following the trigger.
35
- */
36
- triggerHook?: MonoTypeOperatorFunction<Partial<QueryState<Result>> | undefined>;
37
- }
38
- interface Result<A, R> {
39
- status: "idle" | "loading" | "error" | "success";
40
- isLoading: boolean;
41
- /**
42
- * If the latest async query is in a success state, data contains its result.
43
- *
44
- * @important
45
- * The value does not automatically reset when a new async query run. It will be updated
46
- * when a new async query success or error.
47
- */
48
- data: R | undefined;
49
- /**
50
- * If the latest async query is in a error state, error contains its error.
51
- *
52
- * @important
53
- * The value does not automatically reset when a new async query run. It will be updated
54
- * when a new async query success or error.
55
- */
56
- error: unknown | undefined;
57
- mutate: (args: A) => void;
58
- reset: () => void;
59
- }
17
+ __triggerHook?: MonoTypeOperatorFunction<unknown>;
18
+ };
60
19
  /**
61
- * The default value `merge` is suitable for most use case.
62
- * You should not have to worry too much about it and only consider changing
63
- * it when specific need arise.
20
+ * @important
21
+ * Your async query function is cancelled whenever you call a new mutate or
22
+ * when the component is unmounted. Same behavior will happens with your
23
+ * callback functions regarding unmounting. None of them will be called.
24
+ *
25
+ * If you provide an observable as a return it will be automatically cancelled
26
+ * as well during unmount or if called again. If you provide anything else you
27
+ * are in charge of controlling the flow.
64
28
  *
65
- * `merge`:
66
- * Run each async query as they are triggered without any cancellation or queue system.
67
- * The result is always from the latest async query triggered, not necessarily
68
- * the latest one running.
29
+ * If you need to execute async query independently of the component lifecycle or
30
+ * execute functions in parallel you should not use this hook.
69
31
  *
70
- * `concat`:
71
- * Unlike merge, it will trigger each async query sequentially following
72
- * a queue system. The result is not necessarily the last triggered async query
73
- * but the current running async query.
32
+ * @important
33
+ * If you return an observable, the stream will be unsubscribed after receiving
34
+ * the first value. This hook is not meant to be running long running effects.
74
35
  *
75
- * `switch`:
76
- * Only run the latest async query triggered and cancel any previously running one.
77
- * Result correspond to the current running async query.
36
+ * @todo keep async query running on unmount
37
+ * callback should return unmount$ variables
38
+ * options.cancelOnUnmount should be false by default
78
39
  */
79
- type MapOperator = "switch" | "concat" | "merge";
80
- export declare function useAsyncQuery<A = void, R = undefined>(query: (args: A) => Promise<R> | Observable<R>, mapOperatorOrOptions?: MapOperator, options?: AsyncQueryOptions<R, A>): Result<A, R>;
81
- export declare function useAsyncQuery<A = void, R = undefined>(query: (args: A) => Promise<R> | Observable<R>, mapOperatorOrOptions?: AsyncQueryOptions<R, A>): Result<A, R>;
82
- export {};
40
+ export declare function useMutation<Args = void, R = undefined>(options: AsyncQueryOptions<R, Args>): {
41
+ data: R | undefined;
42
+ status: "error" | "idle" | "loading" | "success";
43
+ error: unknown;
44
+ mutate: (mutationArgs: Args) => void;
45
+ reset: () => void;
46
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "reactjrx",
3
3
  "private": false,
4
- "version": "1.54.0",
4
+ "version": "1.55.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"
@@ -37,6 +37,8 @@
37
37
  "rxjs": "*"
38
38
  },
39
39
  "devDependencies": {
40
+ "@testing-library/react": "^14.0.0",
41
+ "@types/node": "^18.15.11",
40
42
  "@types/react": "^18.0.28",
41
43
  "@types/react-dom": "^18.0.11",
42
44
  "@typescript-eslint/eslint-plugin": "^6.11.0",
@@ -57,12 +59,13 @@
57
59
  "typescript": "^5.0.4",
58
60
  "vite": "^4.2.1",
59
61
  "vite-plugin-dts": "^3.6.3",
60
- "vitest": "^0.34.6",
61
- "@testing-library/react": "^14.0.0",
62
- "@types/node": "^18.15.11"
62
+ "vitest": "^0.34.6"
63
63
  },
64
64
  "repository": {
65
65
  "type": "git",
66
66
  "url": "https://github.com/mbret/reactjrx.git"
67
+ },
68
+ "dependencies": {
69
+ "@tanstack/react-query": "^5.8.4"
67
70
  }
68
71
  }