reactjrx 1.29.2 → 1.31.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 +137 -97
- package/dist/index.js +138 -98
- package/dist/lib/binding/useSubject.d.ts +2 -1
- package/dist/lib/queries/useMutation.d.ts +9 -2
- package/dist/lib/state/persistance/withPersistance.d.ts +5 -6
- package/dist/lib/state/signal.d.ts +5 -11
- package/dist/lib/state/useSignal.d.ts +1 -1
- package/dist/lib/state/useSignalValue.d.ts +1 -1
- package/package.json +2 -4
package/dist/index.cjs
CHANGED
|
@@ -101,17 +101,23 @@ const useConstant = (fn) => {
|
|
|
101
101
|
return ref;
|
|
102
102
|
};
|
|
103
103
|
const useSubject = ({
|
|
104
|
-
onBeforeComplete
|
|
104
|
+
onBeforeComplete,
|
|
105
|
+
completeOnUnmount = true
|
|
105
106
|
} = {}) => {
|
|
106
107
|
const subject = useConstant(() => new rxjs.Subject());
|
|
107
108
|
const completed = react.useRef(false);
|
|
108
109
|
const onBeforeCompleteRef = useLiveRef(onBeforeComplete);
|
|
110
|
+
const completeOnUnmountRef = useLiveRef(completeOnUnmount);
|
|
109
111
|
react.useEffect(() => {
|
|
110
112
|
if (completed.current) {
|
|
111
113
|
subject.current = new rxjs.Subject();
|
|
112
114
|
completed.current = false;
|
|
113
115
|
}
|
|
114
116
|
return () => {
|
|
117
|
+
if (!completeOnUnmountRef.current) {
|
|
118
|
+
completed.current = true;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
115
121
|
if (!completed.current) {
|
|
116
122
|
if (onBeforeCompleteRef.current != null)
|
|
117
123
|
onBeforeCompleteRef.current();
|
|
@@ -141,10 +147,8 @@ function trigger(mapper = rxjs.identity) {
|
|
|
141
147
|
}
|
|
142
148
|
const SIGNAL_RESET = Symbol("SIGNAL_RESET");
|
|
143
149
|
function signal(options) {
|
|
144
|
-
const { default: defaultValue
|
|
150
|
+
const { default: defaultValue } = options ?? {};
|
|
145
151
|
const subject = new rxjs.BehaviorSubject(defaultValue);
|
|
146
|
-
const subject$ = subject.asObservable();
|
|
147
|
-
const useValue = () => useObserve(subject$, { defaultValue: subject.getValue(), key });
|
|
148
152
|
const setValue = (arg) => {
|
|
149
153
|
if (arg === subject.getValue())
|
|
150
154
|
return;
|
|
@@ -162,26 +166,24 @@ function signal(options) {
|
|
|
162
166
|
subject.next(arg);
|
|
163
167
|
};
|
|
164
168
|
const getValue = () => subject.getValue();
|
|
165
|
-
return
|
|
166
|
-
|
|
167
|
-
setValue,
|
|
169
|
+
return {
|
|
170
|
+
setState: setValue,
|
|
168
171
|
getValue,
|
|
172
|
+
options,
|
|
169
173
|
/**
|
|
170
174
|
* @important
|
|
171
175
|
* We return the original behavior subject for two reasons:
|
|
172
176
|
* - useObserve may return the default value directly instead of undefined
|
|
173
177
|
* - the scope exist for react binding, this observable is a direct access outside of it
|
|
174
178
|
*/
|
|
175
|
-
subject
|
|
176
|
-
|
|
177
|
-
{ setState: setValue, useState: useValue }
|
|
178
|
-
];
|
|
179
|
+
subject
|
|
180
|
+
};
|
|
179
181
|
}
|
|
180
182
|
const useSetSignal = (signal2) => {
|
|
181
183
|
return signal2.setState;
|
|
182
184
|
};
|
|
183
|
-
const useSignalValue = (signal2) => {
|
|
184
|
-
return signal2.
|
|
185
|
+
const useSignalValue = (signal2, key) => {
|
|
186
|
+
return useObserve(signal2.subject, { defaultValue: signal2.getValue(), key });
|
|
185
187
|
};
|
|
186
188
|
const useSignal = (signal2) => {
|
|
187
189
|
return [useSignalValue(signal2), useSetSignal(signal2)];
|
|
@@ -281,15 +283,14 @@ const getNormalizedPersistanceValue = (unknownValue) => {
|
|
|
281
283
|
return void 0;
|
|
282
284
|
};
|
|
283
285
|
function withPersistance(_signal, { version = 0 } = {}) {
|
|
284
|
-
|
|
285
|
-
if (!options.key) {
|
|
286
|
+
if (!_signal.options.key) {
|
|
286
287
|
console.error(
|
|
287
288
|
"You need to specify a key to use persistance with this signal"
|
|
288
289
|
);
|
|
289
290
|
}
|
|
290
291
|
const hydrateValue = async ({
|
|
291
292
|
adapter,
|
|
292
|
-
key = options.key
|
|
293
|
+
key = _signal.options.key
|
|
293
294
|
}) => {
|
|
294
295
|
if (!key)
|
|
295
296
|
return;
|
|
@@ -300,15 +301,15 @@ function withPersistance(_signal, { version = 0 } = {}) {
|
|
|
300
301
|
if (normalizedValue.migrationVersion && version > normalizedValue.migrationVersion) {
|
|
301
302
|
return;
|
|
302
303
|
}
|
|
303
|
-
|
|
304
|
+
_signal.setState(value.value);
|
|
304
305
|
};
|
|
305
306
|
const persistValue = async ({
|
|
306
307
|
adapter,
|
|
307
|
-
key = options.key
|
|
308
|
+
key = _signal.options.key
|
|
308
309
|
}) => {
|
|
309
310
|
if (!key)
|
|
310
311
|
return;
|
|
311
|
-
const state =
|
|
312
|
+
const state = _signal.getValue();
|
|
312
313
|
const value = {
|
|
313
314
|
value: state,
|
|
314
315
|
__key: "reactjrx_persistance",
|
|
@@ -317,13 +318,14 @@ function withPersistance(_signal, { version = 0 } = {}) {
|
|
|
317
318
|
await adapter.setItem(key, value);
|
|
318
319
|
};
|
|
319
320
|
return [
|
|
320
|
-
{
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
321
|
+
{
|
|
322
|
+
hydrateValue,
|
|
323
|
+
persistValue,
|
|
324
|
+
setValue: _signal.setState,
|
|
325
|
+
$: _signal.subject,
|
|
326
|
+
options: _signal.options
|
|
327
|
+
},
|
|
328
|
+
_signal
|
|
327
329
|
];
|
|
328
330
|
}
|
|
329
331
|
const normalizeStore = (store) => {
|
|
@@ -435,9 +437,40 @@ const useBehaviorSubject = (state) => {
|
|
|
435
437
|
}, []);
|
|
436
438
|
return subject;
|
|
437
439
|
};
|
|
440
|
+
function shallowEqual(objA, objB) {
|
|
441
|
+
if (objA === null || objA === void 0 || objB === null || objB === void 0) {
|
|
442
|
+
return objA === objB;
|
|
443
|
+
}
|
|
444
|
+
if (typeof objA !== "object" || typeof objB !== "object") {
|
|
445
|
+
return objA === objB;
|
|
446
|
+
}
|
|
447
|
+
if (objA.constructor !== objB.constructor) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
const keysA = Object.keys(objA);
|
|
451
|
+
const keysB = Object.keys(objB);
|
|
452
|
+
if (keysA.length !== keysB.length) {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
for (const key of keysA) {
|
|
456
|
+
if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
|
|
457
|
+
return false;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return true;
|
|
461
|
+
}
|
|
438
462
|
function useMutation(query, mapOperatorOrOptions, options = {}) {
|
|
439
463
|
const queryRef = useLiveRef(query);
|
|
440
464
|
const triggerSubject = useSubject();
|
|
465
|
+
const resetSubject = useSubject({
|
|
466
|
+
/**
|
|
467
|
+
* @important
|
|
468
|
+
* Because mutation can still run after unmount, the user might
|
|
469
|
+
* want to use reset for whatever reason. We will only manually complete
|
|
470
|
+
* this subject whenever the main query hook finalize.
|
|
471
|
+
*/
|
|
472
|
+
completeOnUnmount: false
|
|
473
|
+
});
|
|
441
474
|
const optionsRef = useLiveRef(
|
|
442
475
|
typeof mapOperatorOrOptions === "object" ? mapOperatorOrOptions : options
|
|
443
476
|
);
|
|
@@ -449,55 +482,81 @@ function useMutation(query, mapOperatorOrOptions, options = {}) {
|
|
|
449
482
|
const mapOperator = typeof mapOperatorOrOptions === "string" ? mapOperatorOrOptions : "merge";
|
|
450
483
|
react.useEffect(() => {
|
|
451
484
|
const switchOperator = mapOperator === "concat" ? rxjs.concatMap : mapOperator === "switch" ? rxjs.switchMap : rxjs.mergeMap;
|
|
452
|
-
const subscription =
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
data$.current.next({
|
|
460
|
-
...data$.current.getValue(),
|
|
461
|
-
status: "loading"
|
|
462
|
-
});
|
|
463
|
-
return rxjs.combineLatest([
|
|
464
|
-
rxjs.defer(() => rxjs.from(queryRef.current(args))).pipe(
|
|
465
|
-
querx(optionsRef.current),
|
|
466
|
-
rxjs.first(),
|
|
467
|
-
rxjs.map((data) => ({ data, isError: false })),
|
|
468
|
-
rxjs.catchError((error) => {
|
|
469
|
-
if (optionsRef.current.onError != null) {
|
|
470
|
-
optionsRef.current.onError(error);
|
|
471
|
-
}
|
|
472
|
-
return rxjs.of({ data: error, isError: true });
|
|
473
|
-
})
|
|
474
|
-
),
|
|
475
|
-
isLastMutationCalled
|
|
476
|
-
]).pipe(
|
|
477
|
-
rxjs.tap(([{ data, isError }, isLastMutationCalled2]) => {
|
|
478
|
-
if (!isError) {
|
|
479
|
-
if (optionsRef.current.onSuccess != null)
|
|
480
|
-
optionsRef.current.onSuccess(data);
|
|
481
|
-
}
|
|
482
|
-
if (isLastMutationCalled2) {
|
|
483
|
-
data$.current.next({
|
|
484
|
-
...data$.current.getValue(),
|
|
485
|
-
...isError ? {
|
|
486
|
-
status: "error",
|
|
487
|
-
error: data,
|
|
488
|
-
data: void 0
|
|
489
|
-
} : {
|
|
490
|
-
status: "success",
|
|
491
|
-
error: void 0,
|
|
492
|
-
data
|
|
493
|
-
}
|
|
494
|
-
});
|
|
495
|
-
}
|
|
485
|
+
const subscription = rxjs.merge(
|
|
486
|
+
resetSubject.current.pipe(
|
|
487
|
+
rxjs.map(
|
|
488
|
+
() => ({
|
|
489
|
+
status: "idle",
|
|
490
|
+
data: void 0,
|
|
491
|
+
error: void 0
|
|
496
492
|
})
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
493
|
+
)
|
|
494
|
+
),
|
|
495
|
+
triggerSubject.current.pipe(
|
|
496
|
+
switchOperator((args) => {
|
|
497
|
+
const isLastMutationCalled = triggerSubject.current.pipe(
|
|
498
|
+
rxjs.take(1),
|
|
499
|
+
rxjs.map(() => mapOperator === "concat"),
|
|
500
|
+
rxjs.startWith(true)
|
|
501
|
+
);
|
|
502
|
+
return rxjs.merge(
|
|
503
|
+
rxjs.of({
|
|
504
|
+
status: "loading"
|
|
505
|
+
}),
|
|
506
|
+
rxjs.combineLatest([
|
|
507
|
+
rxjs.defer(() => rxjs.from(queryRef.current(args))).pipe(
|
|
508
|
+
querx(optionsRef.current),
|
|
509
|
+
rxjs.first(),
|
|
510
|
+
rxjs.map((data) => ({ data, isError: false })),
|
|
511
|
+
rxjs.catchError((error) => {
|
|
512
|
+
if (optionsRef.current.onError != null) {
|
|
513
|
+
optionsRef.current.onError(error);
|
|
514
|
+
}
|
|
515
|
+
return rxjs.of({ data: error, isError: true });
|
|
516
|
+
})
|
|
517
|
+
),
|
|
518
|
+
isLastMutationCalled
|
|
519
|
+
]).pipe(
|
|
520
|
+
rxjs.map(([{ data, isError }, isLastMutationCalled2]) => {
|
|
521
|
+
if (!isError) {
|
|
522
|
+
if (optionsRef.current.onSuccess != null)
|
|
523
|
+
optionsRef.current.onSuccess(data);
|
|
524
|
+
}
|
|
525
|
+
if (isLastMutationCalled2) {
|
|
526
|
+
return isError ? {
|
|
527
|
+
status: "error",
|
|
528
|
+
error: data,
|
|
529
|
+
data: void 0
|
|
530
|
+
} : {
|
|
531
|
+
status: "success",
|
|
532
|
+
error: void 0,
|
|
533
|
+
data
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
return void 0;
|
|
537
|
+
}),
|
|
538
|
+
rxjs.takeUntil(resetSubject.current)
|
|
539
|
+
)
|
|
540
|
+
);
|
|
541
|
+
}),
|
|
542
|
+
optionsRef.current.triggerHook ?? rxjs.identity,
|
|
543
|
+
rxjs.finalize(() => {
|
|
544
|
+
resetSubject.current.complete();
|
|
545
|
+
})
|
|
546
|
+
)
|
|
547
|
+
).pipe(
|
|
548
|
+
/**
|
|
549
|
+
* @important
|
|
550
|
+
* state update optimization
|
|
551
|
+
*/
|
|
552
|
+
rxjs.distinctUntilChanged(shallowEqual),
|
|
553
|
+
rxjs.filter((state) => !!state && !!Object.keys(state).length)
|
|
554
|
+
).subscribe((state) => {
|
|
555
|
+
data$.current.next({
|
|
556
|
+
...data$.current.getValue(),
|
|
557
|
+
...state
|
|
558
|
+
});
|
|
559
|
+
});
|
|
501
560
|
return () => {
|
|
502
561
|
if (optionsRef.current.cancelOnUnMount) {
|
|
503
562
|
subscription.unsubscribe();
|
|
@@ -514,31 +573,12 @@ function useMutation(query, mapOperatorOrOptions, options = {}) {
|
|
|
514
573
|
const mutate = react.useCallback((arg) => {
|
|
515
574
|
triggerSubject.current.next(arg);
|
|
516
575
|
}, []);
|
|
517
|
-
|
|
576
|
+
const reset = react.useCallback(() => {
|
|
577
|
+
resetSubject.current.next();
|
|
578
|
+
}, []);
|
|
579
|
+
return { ...result, isLoading: result.status === "loading", mutate, reset };
|
|
518
580
|
}
|
|
519
581
|
const arrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
520
|
-
function shallowEqual(objA, objB) {
|
|
521
|
-
if (objA === null || objA === void 0 || objB === null || objB === void 0) {
|
|
522
|
-
return objA === objB;
|
|
523
|
-
}
|
|
524
|
-
if (typeof objA !== "object" || typeof objB !== "object") {
|
|
525
|
-
return objA === objB;
|
|
526
|
-
}
|
|
527
|
-
if (objA.constructor !== objB.constructor) {
|
|
528
|
-
return false;
|
|
529
|
-
}
|
|
530
|
-
const keysA = Object.keys(objA);
|
|
531
|
-
const keysB = Object.keys(objB);
|
|
532
|
-
if (keysA.length !== keysB.length) {
|
|
533
|
-
return false;
|
|
534
|
-
}
|
|
535
|
-
for (const key of keysA) {
|
|
536
|
-
if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
|
|
537
|
-
return false;
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
return true;
|
|
541
|
-
}
|
|
542
582
|
const useCreateCacheStore = () => {
|
|
543
583
|
const cacheStore = useBehaviorSubject({});
|
|
544
584
|
useSubscribe(
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRef, useMemo, useCallback, useSyncExternalStore, useEffect, createContext, memo, useContext, useState } from "react";
|
|
2
|
-
import { distinctUntilChanged, tap, finalize, catchError, EMPTY, Subject, identity, BehaviorSubject, of, zip, from, map, merge, throttleTime, switchMap, defer, iif, timer, throwError, take, startWith, combineLatest, first, concatMap as concatMap$1, mergeMap, skip, interval, withLatestFrom, shareReplay, repeat,
|
|
2
|
+
import { distinctUntilChanged, tap, finalize, catchError, EMPTY, Subject, identity, BehaviorSubject, of, zip, from, map, merge, throttleTime, switchMap, defer, iif, timer, throwError, take, startWith, combineLatest, first, takeUntil, filter, concatMap as concatMap$1, mergeMap, skip, interval, withLatestFrom, shareReplay, repeat, share, retry } from "rxjs";
|
|
3
3
|
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
import { retryWhen, concatMap, tap as tap$1 } from "rxjs/operators";
|
|
5
5
|
const useLiveRef = (value) => {
|
|
@@ -99,17 +99,23 @@ const useConstant = (fn) => {
|
|
|
99
99
|
return ref;
|
|
100
100
|
};
|
|
101
101
|
const useSubject = ({
|
|
102
|
-
onBeforeComplete
|
|
102
|
+
onBeforeComplete,
|
|
103
|
+
completeOnUnmount = true
|
|
103
104
|
} = {}) => {
|
|
104
105
|
const subject = useConstant(() => new Subject());
|
|
105
106
|
const completed = useRef(false);
|
|
106
107
|
const onBeforeCompleteRef = useLiveRef(onBeforeComplete);
|
|
108
|
+
const completeOnUnmountRef = useLiveRef(completeOnUnmount);
|
|
107
109
|
useEffect(() => {
|
|
108
110
|
if (completed.current) {
|
|
109
111
|
subject.current = new Subject();
|
|
110
112
|
completed.current = false;
|
|
111
113
|
}
|
|
112
114
|
return () => {
|
|
115
|
+
if (!completeOnUnmountRef.current) {
|
|
116
|
+
completed.current = true;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
113
119
|
if (!completed.current) {
|
|
114
120
|
if (onBeforeCompleteRef.current != null)
|
|
115
121
|
onBeforeCompleteRef.current();
|
|
@@ -139,10 +145,8 @@ function trigger(mapper = identity) {
|
|
|
139
145
|
}
|
|
140
146
|
const SIGNAL_RESET = Symbol("SIGNAL_RESET");
|
|
141
147
|
function signal(options) {
|
|
142
|
-
const { default: defaultValue
|
|
148
|
+
const { default: defaultValue } = options ?? {};
|
|
143
149
|
const subject = new BehaviorSubject(defaultValue);
|
|
144
|
-
const subject$ = subject.asObservable();
|
|
145
|
-
const useValue = () => useObserve(subject$, { defaultValue: subject.getValue(), key });
|
|
146
150
|
const setValue = (arg) => {
|
|
147
151
|
if (arg === subject.getValue())
|
|
148
152
|
return;
|
|
@@ -160,26 +164,24 @@ function signal(options) {
|
|
|
160
164
|
subject.next(arg);
|
|
161
165
|
};
|
|
162
166
|
const getValue = () => subject.getValue();
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
setValue,
|
|
167
|
+
return {
|
|
168
|
+
setState: setValue,
|
|
166
169
|
getValue,
|
|
170
|
+
options,
|
|
167
171
|
/**
|
|
168
172
|
* @important
|
|
169
173
|
* We return the original behavior subject for two reasons:
|
|
170
174
|
* - useObserve may return the default value directly instead of undefined
|
|
171
175
|
* - the scope exist for react binding, this observable is a direct access outside of it
|
|
172
176
|
*/
|
|
173
|
-
subject
|
|
174
|
-
|
|
175
|
-
{ setState: setValue, useState: useValue }
|
|
176
|
-
];
|
|
177
|
+
subject
|
|
178
|
+
};
|
|
177
179
|
}
|
|
178
180
|
const useSetSignal = (signal2) => {
|
|
179
181
|
return signal2.setState;
|
|
180
182
|
};
|
|
181
|
-
const useSignalValue = (signal2) => {
|
|
182
|
-
return signal2.
|
|
183
|
+
const useSignalValue = (signal2, key) => {
|
|
184
|
+
return useObserve(signal2.subject, { defaultValue: signal2.getValue(), key });
|
|
183
185
|
};
|
|
184
186
|
const useSignal = (signal2) => {
|
|
185
187
|
return [useSignalValue(signal2), useSetSignal(signal2)];
|
|
@@ -279,15 +281,14 @@ const getNormalizedPersistanceValue = (unknownValue) => {
|
|
|
279
281
|
return void 0;
|
|
280
282
|
};
|
|
281
283
|
function withPersistance(_signal, { version = 0 } = {}) {
|
|
282
|
-
|
|
283
|
-
if (!options.key) {
|
|
284
|
+
if (!_signal.options.key) {
|
|
284
285
|
console.error(
|
|
285
286
|
"You need to specify a key to use persistance with this signal"
|
|
286
287
|
);
|
|
287
288
|
}
|
|
288
289
|
const hydrateValue = async ({
|
|
289
290
|
adapter,
|
|
290
|
-
key = options.key
|
|
291
|
+
key = _signal.options.key
|
|
291
292
|
}) => {
|
|
292
293
|
if (!key)
|
|
293
294
|
return;
|
|
@@ -298,15 +299,15 @@ function withPersistance(_signal, { version = 0 } = {}) {
|
|
|
298
299
|
if (normalizedValue.migrationVersion && version > normalizedValue.migrationVersion) {
|
|
299
300
|
return;
|
|
300
301
|
}
|
|
301
|
-
|
|
302
|
+
_signal.setState(value.value);
|
|
302
303
|
};
|
|
303
304
|
const persistValue = async ({
|
|
304
305
|
adapter,
|
|
305
|
-
key = options.key
|
|
306
|
+
key = _signal.options.key
|
|
306
307
|
}) => {
|
|
307
308
|
if (!key)
|
|
308
309
|
return;
|
|
309
|
-
const state =
|
|
310
|
+
const state = _signal.getValue();
|
|
310
311
|
const value = {
|
|
311
312
|
value: state,
|
|
312
313
|
__key: "reactjrx_persistance",
|
|
@@ -315,13 +316,14 @@ function withPersistance(_signal, { version = 0 } = {}) {
|
|
|
315
316
|
await adapter.setItem(key, value);
|
|
316
317
|
};
|
|
317
318
|
return [
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
319
|
+
{
|
|
320
|
+
hydrateValue,
|
|
321
|
+
persistValue,
|
|
322
|
+
setValue: _signal.setState,
|
|
323
|
+
$: _signal.subject,
|
|
324
|
+
options: _signal.options
|
|
325
|
+
},
|
|
326
|
+
_signal
|
|
325
327
|
];
|
|
326
328
|
}
|
|
327
329
|
const normalizeStore = (store) => {
|
|
@@ -433,9 +435,40 @@ const useBehaviorSubject = (state) => {
|
|
|
433
435
|
}, []);
|
|
434
436
|
return subject;
|
|
435
437
|
};
|
|
438
|
+
function shallowEqual(objA, objB) {
|
|
439
|
+
if (objA === null || objA === void 0 || objB === null || objB === void 0) {
|
|
440
|
+
return objA === objB;
|
|
441
|
+
}
|
|
442
|
+
if (typeof objA !== "object" || typeof objB !== "object") {
|
|
443
|
+
return objA === objB;
|
|
444
|
+
}
|
|
445
|
+
if (objA.constructor !== objB.constructor) {
|
|
446
|
+
return false;
|
|
447
|
+
}
|
|
448
|
+
const keysA = Object.keys(objA);
|
|
449
|
+
const keysB = Object.keys(objB);
|
|
450
|
+
if (keysA.length !== keysB.length) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
for (const key of keysA) {
|
|
454
|
+
if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
436
460
|
function useMutation(query, mapOperatorOrOptions, options = {}) {
|
|
437
461
|
const queryRef = useLiveRef(query);
|
|
438
462
|
const triggerSubject = useSubject();
|
|
463
|
+
const resetSubject = useSubject({
|
|
464
|
+
/**
|
|
465
|
+
* @important
|
|
466
|
+
* Because mutation can still run after unmount, the user might
|
|
467
|
+
* want to use reset for whatever reason. We will only manually complete
|
|
468
|
+
* this subject whenever the main query hook finalize.
|
|
469
|
+
*/
|
|
470
|
+
completeOnUnmount: false
|
|
471
|
+
});
|
|
439
472
|
const optionsRef = useLiveRef(
|
|
440
473
|
typeof mapOperatorOrOptions === "object" ? mapOperatorOrOptions : options
|
|
441
474
|
);
|
|
@@ -447,55 +480,81 @@ function useMutation(query, mapOperatorOrOptions, options = {}) {
|
|
|
447
480
|
const mapOperator = typeof mapOperatorOrOptions === "string" ? mapOperatorOrOptions : "merge";
|
|
448
481
|
useEffect(() => {
|
|
449
482
|
const switchOperator = mapOperator === "concat" ? concatMap$1 : mapOperator === "switch" ? switchMap : mergeMap;
|
|
450
|
-
const subscription =
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
data$.current.next({
|
|
458
|
-
...data$.current.getValue(),
|
|
459
|
-
status: "loading"
|
|
460
|
-
});
|
|
461
|
-
return combineLatest([
|
|
462
|
-
defer(() => from(queryRef.current(args))).pipe(
|
|
463
|
-
querx(optionsRef.current),
|
|
464
|
-
first(),
|
|
465
|
-
map((data) => ({ data, isError: false })),
|
|
466
|
-
catchError((error) => {
|
|
467
|
-
if (optionsRef.current.onError != null) {
|
|
468
|
-
optionsRef.current.onError(error);
|
|
469
|
-
}
|
|
470
|
-
return of({ data: error, isError: true });
|
|
471
|
-
})
|
|
472
|
-
),
|
|
473
|
-
isLastMutationCalled
|
|
474
|
-
]).pipe(
|
|
475
|
-
tap(([{ data, isError }, isLastMutationCalled2]) => {
|
|
476
|
-
if (!isError) {
|
|
477
|
-
if (optionsRef.current.onSuccess != null)
|
|
478
|
-
optionsRef.current.onSuccess(data);
|
|
479
|
-
}
|
|
480
|
-
if (isLastMutationCalled2) {
|
|
481
|
-
data$.current.next({
|
|
482
|
-
...data$.current.getValue(),
|
|
483
|
-
...isError ? {
|
|
484
|
-
status: "error",
|
|
485
|
-
error: data,
|
|
486
|
-
data: void 0
|
|
487
|
-
} : {
|
|
488
|
-
status: "success",
|
|
489
|
-
error: void 0,
|
|
490
|
-
data
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
}
|
|
483
|
+
const subscription = merge(
|
|
484
|
+
resetSubject.current.pipe(
|
|
485
|
+
map(
|
|
486
|
+
() => ({
|
|
487
|
+
status: "idle",
|
|
488
|
+
data: void 0,
|
|
489
|
+
error: void 0
|
|
494
490
|
})
|
|
495
|
-
)
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
491
|
+
)
|
|
492
|
+
),
|
|
493
|
+
triggerSubject.current.pipe(
|
|
494
|
+
switchOperator((args) => {
|
|
495
|
+
const isLastMutationCalled = triggerSubject.current.pipe(
|
|
496
|
+
take(1),
|
|
497
|
+
map(() => mapOperator === "concat"),
|
|
498
|
+
startWith(true)
|
|
499
|
+
);
|
|
500
|
+
return merge(
|
|
501
|
+
of({
|
|
502
|
+
status: "loading"
|
|
503
|
+
}),
|
|
504
|
+
combineLatest([
|
|
505
|
+
defer(() => from(queryRef.current(args))).pipe(
|
|
506
|
+
querx(optionsRef.current),
|
|
507
|
+
first(),
|
|
508
|
+
map((data) => ({ data, isError: false })),
|
|
509
|
+
catchError((error) => {
|
|
510
|
+
if (optionsRef.current.onError != null) {
|
|
511
|
+
optionsRef.current.onError(error);
|
|
512
|
+
}
|
|
513
|
+
return of({ data: error, isError: true });
|
|
514
|
+
})
|
|
515
|
+
),
|
|
516
|
+
isLastMutationCalled
|
|
517
|
+
]).pipe(
|
|
518
|
+
map(([{ data, isError }, isLastMutationCalled2]) => {
|
|
519
|
+
if (!isError) {
|
|
520
|
+
if (optionsRef.current.onSuccess != null)
|
|
521
|
+
optionsRef.current.onSuccess(data);
|
|
522
|
+
}
|
|
523
|
+
if (isLastMutationCalled2) {
|
|
524
|
+
return isError ? {
|
|
525
|
+
status: "error",
|
|
526
|
+
error: data,
|
|
527
|
+
data: void 0
|
|
528
|
+
} : {
|
|
529
|
+
status: "success",
|
|
530
|
+
error: void 0,
|
|
531
|
+
data
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
return void 0;
|
|
535
|
+
}),
|
|
536
|
+
takeUntil(resetSubject.current)
|
|
537
|
+
)
|
|
538
|
+
);
|
|
539
|
+
}),
|
|
540
|
+
optionsRef.current.triggerHook ?? identity,
|
|
541
|
+
finalize(() => {
|
|
542
|
+
resetSubject.current.complete();
|
|
543
|
+
})
|
|
544
|
+
)
|
|
545
|
+
).pipe(
|
|
546
|
+
/**
|
|
547
|
+
* @important
|
|
548
|
+
* state update optimization
|
|
549
|
+
*/
|
|
550
|
+
distinctUntilChanged(shallowEqual),
|
|
551
|
+
filter((state) => !!state && !!Object.keys(state).length)
|
|
552
|
+
).subscribe((state) => {
|
|
553
|
+
data$.current.next({
|
|
554
|
+
...data$.current.getValue(),
|
|
555
|
+
...state
|
|
556
|
+
});
|
|
557
|
+
});
|
|
499
558
|
return () => {
|
|
500
559
|
if (optionsRef.current.cancelOnUnMount) {
|
|
501
560
|
subscription.unsubscribe();
|
|
@@ -512,31 +571,12 @@ function useMutation(query, mapOperatorOrOptions, options = {}) {
|
|
|
512
571
|
const mutate = useCallback((arg) => {
|
|
513
572
|
triggerSubject.current.next(arg);
|
|
514
573
|
}, []);
|
|
515
|
-
|
|
574
|
+
const reset = useCallback(() => {
|
|
575
|
+
resetSubject.current.next();
|
|
576
|
+
}, []);
|
|
577
|
+
return { ...result, isLoading: result.status === "loading", mutate, reset };
|
|
516
578
|
}
|
|
517
579
|
const arrayEqual = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
518
|
-
function shallowEqual(objA, objB) {
|
|
519
|
-
if (objA === null || objA === void 0 || objB === null || objB === void 0) {
|
|
520
|
-
return objA === objB;
|
|
521
|
-
}
|
|
522
|
-
if (typeof objA !== "object" || typeof objB !== "object") {
|
|
523
|
-
return objA === objB;
|
|
524
|
-
}
|
|
525
|
-
if (objA.constructor !== objB.constructor) {
|
|
526
|
-
return false;
|
|
527
|
-
}
|
|
528
|
-
const keysA = Object.keys(objA);
|
|
529
|
-
const keysB = Object.keys(objB);
|
|
530
|
-
if (keysA.length !== keysB.length) {
|
|
531
|
-
return false;
|
|
532
|
-
}
|
|
533
|
-
for (const key of keysA) {
|
|
534
|
-
if (!objB.hasOwnProperty(key) || objA[key] !== objB[key]) {
|
|
535
|
-
return false;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
return true;
|
|
539
|
-
}
|
|
540
580
|
const useCreateCacheStore = () => {
|
|
541
581
|
const cacheStore = useBehaviorSubject({});
|
|
542
582
|
useSubscribe(
|
|
@@ -3,8 +3,9 @@ import { Subject } from "rxjs";
|
|
|
3
3
|
* @see
|
|
4
4
|
* useBehaviorSubject
|
|
5
5
|
*/
|
|
6
|
-
export declare const useSubject: <S>({ onBeforeComplete }?: {
|
|
6
|
+
export declare const useSubject: <S>({ onBeforeComplete, completeOnUnmount }?: {
|
|
7
7
|
onBeforeComplete?: (() => void) | undefined;
|
|
8
|
+
completeOnUnmount?: boolean | undefined;
|
|
8
9
|
}) => {
|
|
9
10
|
current: Subject<S>;
|
|
10
11
|
};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
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
|
+
}
|
|
2
7
|
export interface MutationOptions<R> {
|
|
3
8
|
retry?: false | number | ((attempt: number, error: unknown) => boolean);
|
|
4
9
|
/**
|
|
@@ -25,9 +30,10 @@ export interface MutationOptions<R> {
|
|
|
25
30
|
*/
|
|
26
31
|
cancelOnUnMount?: boolean;
|
|
27
32
|
/**
|
|
28
|
-
* Only use for debugging
|
|
33
|
+
* Only use for debugging.
|
|
34
|
+
* It is not the main subscription hook, only the one following the trigger.
|
|
29
35
|
*/
|
|
30
|
-
|
|
36
|
+
triggerHook?: MonoTypeOperatorFunction<Partial<QueryState<R>> | undefined>;
|
|
31
37
|
}
|
|
32
38
|
interface Result<A, R> {
|
|
33
39
|
status: "idle" | "loading" | "error" | "success";
|
|
@@ -49,6 +55,7 @@ interface Result<A, R> {
|
|
|
49
55
|
*/
|
|
50
56
|
error: unknown | undefined;
|
|
51
57
|
mutate: (args: A) => void;
|
|
58
|
+
reset: () => void;
|
|
52
59
|
}
|
|
53
60
|
/**
|
|
54
61
|
* The default value `merge` is suitable for most use case.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type signal } from "../signal";
|
|
1
|
+
import type { Signal } from "../signal";
|
|
3
2
|
import { type Adapter } from "./types";
|
|
4
3
|
export interface WithPersistanceReturn<T> {
|
|
5
4
|
hydrateValue: (params: {
|
|
@@ -8,12 +7,12 @@ export interface WithPersistanceReturn<T> {
|
|
|
8
7
|
persistValue: (params: {
|
|
9
8
|
adapter: Adapter;
|
|
10
9
|
}) => Promise<void>;
|
|
11
|
-
setValue:
|
|
12
|
-
$:
|
|
10
|
+
setValue: Signal<T, T>['setState'];
|
|
11
|
+
$: Signal<T, T>['subject'];
|
|
13
12
|
options: {
|
|
14
13
|
key?: string;
|
|
15
14
|
};
|
|
16
15
|
}
|
|
17
|
-
export declare function withPersistance<T>(_signal:
|
|
16
|
+
export declare function withPersistance<T>(_signal: Signal<T, T>, { version }?: {
|
|
18
17
|
version?: number;
|
|
19
|
-
}): [WithPersistanceReturn<T>,
|
|
18
|
+
}): [WithPersistanceReturn<T>, Signal<T, T>];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { Observable } from "rxjs";
|
|
2
2
|
import { SIGNAL_RESET } from "./constants";
|
|
3
3
|
type Option<R = undefined> = {
|
|
4
4
|
key?: string;
|
|
@@ -10,15 +10,9 @@ type Option<R = undefined> = {
|
|
|
10
10
|
type SetState<S> = (stateOrUpdater: typeof SIGNAL_RESET | S | ((prev: S) => S)) => void;
|
|
11
11
|
export interface Signal<S, R> {
|
|
12
12
|
setState: SetState<S>;
|
|
13
|
-
|
|
13
|
+
getValue: () => R;
|
|
14
|
+
options: Option<S>;
|
|
15
|
+
subject: Observable<S>;
|
|
14
16
|
}
|
|
15
|
-
|
|
16
|
-
() => R,
|
|
17
|
-
SetState<S>,
|
|
18
|
-
() => R,
|
|
19
|
-
Observable<R>,
|
|
20
|
-
Option<S>,
|
|
21
|
-
Signal<S, R>
|
|
22
|
-
];
|
|
23
|
-
export declare function signal<T = undefined>(options: Option<T>): Return<T, T>;
|
|
17
|
+
export declare function signal<T = undefined>(options: Option<T>): Signal<T, T>;
|
|
24
18
|
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type Signal } from "./signal";
|
|
2
|
-
export declare const useSignal: <S, R>(signal: Signal<S, R>) => readonly [R, (stateOrUpdater: typeof import("./constants").SIGNAL_RESET | S | ((prev: S) => S)) => void];
|
|
2
|
+
export declare const useSignal: <S, R>(signal: Signal<S, R>) => readonly [S | R, (stateOrUpdater: typeof import("./constants").SIGNAL_RESET | S | ((prev: S) => S)) => void];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { type Signal } from "./signal";
|
|
2
|
-
export declare const useSignalValue: <S, R>(signal: Signal<S, R
|
|
2
|
+
export declare const useSignalValue: <S, R>(signal: Signal<S, R>, key?: string) => S | R;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reactjrx",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.31.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
@@ -57,9 +57,7 @@
|
|
|
57
57
|
"typescript": "^5.0.4",
|
|
58
58
|
"vite": "^4.2.1",
|
|
59
59
|
"vite-plugin-dts": "^2.2.0",
|
|
60
|
-
"vitest": "^0.31.0"
|
|
61
|
-
},
|
|
62
|
-
"dependencies": {
|
|
60
|
+
"vitest": "^0.31.0",
|
|
63
61
|
"@testing-library/react": "^14.0.0",
|
|
64
62
|
"@types/node": "^18.15.11"
|
|
65
63
|
},
|