react-rx 3.0.0-1 → 3.0.0-3
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/cjs/useObservable.d.ts +4 -4
- package/dist/cjs/useObservable.js +21 -42
- package/dist/es2015/useObservable.d.ts +4 -4
- package/dist/es2015/useObservable.js +21 -42
- package/dist/esm/useObservable.d.ts +4 -4
- package/dist/esm/useObservable.js +21 -42
- package/package.json +3 -3
- package/src/useObservable.ts +36 -59
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
export declare function useObservable<
|
|
3
|
-
|
|
4
|
-
export
|
|
1
|
+
import type { Observable } from 'rxjs';
|
|
2
|
+
export declare function useObservable<ObservableType extends Observable<any>>(observable: ObservableType, initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>)): UnboxObservable<ObservableType>;
|
|
3
|
+
declare type UnboxObservable<T> = T extends Observable<infer U> ? U : never;
|
|
4
|
+
export {};
|
|
@@ -7,16 +7,6 @@ function getValue(value) {
|
|
|
7
7
|
return typeof value === 'function' ? value() : value;
|
|
8
8
|
}
|
|
9
9
|
var cache = new WeakMap();
|
|
10
|
-
function getOrCreateStore(inputObservable, initialValue) {
|
|
11
|
-
if (!cache.has(inputObservable)) {
|
|
12
|
-
var entry_1 = { currentValue: initialValue };
|
|
13
|
-
entry_1.observable = inputObservable.pipe((0, operators_1.shareReplay)({ refCount: true, bufferSize: 1 }), (0, operators_1.tap)(function (value) { return (entry_1.currentValue = value); }));
|
|
14
|
-
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns
|
|
15
|
-
entry_1.subscription = entry_1.observable.subscribe();
|
|
16
|
-
cache.set(inputObservable, entry_1);
|
|
17
|
-
}
|
|
18
|
-
return cache.get(inputObservable);
|
|
19
|
-
}
|
|
20
10
|
function useObservable(observable, initialValue) {
|
|
21
11
|
/**
|
|
22
12
|
* Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
|
|
@@ -29,41 +19,30 @@ function useObservable(observable, initialValue) {
|
|
|
29
19
|
(0, react_1.useEffect)(function () {
|
|
30
20
|
initialValueRef.current = getValue(initialValue);
|
|
31
21
|
}, [initialValue]);
|
|
32
|
-
var
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
22
|
+
var store = (0, react_1.useMemo)(function () {
|
|
23
|
+
if (!cache.has(observable)) {
|
|
24
|
+
var entry_1 = {
|
|
25
|
+
snapshot: initialValueRef.current,
|
|
26
|
+
};
|
|
27
|
+
entry_1.observable = observable.pipe((0, operators_1.shareReplay)({ refCount: true, bufferSize: 1 }), (0, operators_1.tap)(function (value) { return (entry_1.snapshot = value); }));
|
|
28
|
+
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns, and keep the observable alive until the component unmounts.
|
|
29
|
+
entry_1.subscription = entry_1.observable.subscribe();
|
|
30
|
+
cache.set(observable, entry_1);
|
|
36
31
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
},
|
|
42
|
-
function subscribe(callback) {
|
|
43
|
-
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here as we only need 1 subscription active to keep the observer alive
|
|
44
|
-
var sub = store.observable.subscribe(callback);
|
|
45
|
-
return function () {
|
|
46
|
-
sub.unsubscribe();
|
|
47
|
-
};
|
|
48
|
-
},
|
|
49
|
-
];
|
|
50
|
-
}, [observable]), getSnapshot = _a[0], subscribe = _a[1];
|
|
51
|
-
var shouldRestoreSubscriptionRef = (0, react_1.useRef)(false);
|
|
52
|
-
(0, react_1.useEffect)(function () {
|
|
53
|
-
var store = getOrCreateStore(observable, initialValueRef.current);
|
|
54
|
-
if (shouldRestoreSubscriptionRef.current) {
|
|
55
|
-
if (store.subscription.closed) {
|
|
56
|
-
store.subscription = store.observable.subscribe();
|
|
57
|
-
}
|
|
58
|
-
shouldRestoreSubscriptionRef.current = false;
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
33
|
+
var instance = cache.get(observable);
|
|
34
|
+
if (instance.subscription.closed) {
|
|
35
|
+
instance.subscription = instance.observable.subscribe();
|
|
59
36
|
}
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
37
|
+
return {
|
|
38
|
+
subscribe: function (onStoreChange) {
|
|
39
|
+
var subscription = instance.observable.subscribe(onStoreChange);
|
|
40
|
+
instance.subscription.unsubscribe();
|
|
41
|
+
return function () { return subscription.unsubscribe(); };
|
|
42
|
+
},
|
|
43
|
+
getSnapshot: function () { return instance.snapshot; },
|
|
65
44
|
};
|
|
66
45
|
}, [observable]);
|
|
67
|
-
return (0, react_1.useSyncExternalStore)(subscribe, getSnapshot);
|
|
46
|
+
return (0, react_1.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
68
47
|
}
|
|
69
48
|
exports.useObservable = useObservable;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
export declare function useObservable<
|
|
3
|
-
|
|
4
|
-
export
|
|
1
|
+
import type { Observable } from 'rxjs';
|
|
2
|
+
export declare function useObservable<ObservableType extends Observable<any>>(observable: ObservableType, initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>)): UnboxObservable<ObservableType>;
|
|
3
|
+
declare type UnboxObservable<T> = T extends Observable<infer U> ? U : never;
|
|
4
|
+
export {};
|
|
@@ -4,16 +4,6 @@ function getValue(value) {
|
|
|
4
4
|
return typeof value === 'function' ? value() : value;
|
|
5
5
|
}
|
|
6
6
|
const cache = new WeakMap();
|
|
7
|
-
function getOrCreateStore(inputObservable, initialValue) {
|
|
8
|
-
if (!cache.has(inputObservable)) {
|
|
9
|
-
const entry = { currentValue: initialValue };
|
|
10
|
-
entry.observable = inputObservable.pipe(shareReplay({ refCount: true, bufferSize: 1 }), tap(value => (entry.currentValue = value)));
|
|
11
|
-
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns
|
|
12
|
-
entry.subscription = entry.observable.subscribe();
|
|
13
|
-
cache.set(inputObservable, entry);
|
|
14
|
-
}
|
|
15
|
-
return cache.get(inputObservable);
|
|
16
|
-
}
|
|
17
7
|
export function useObservable(observable, initialValue) {
|
|
18
8
|
/**
|
|
19
9
|
* Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
|
|
@@ -26,40 +16,29 @@ export function useObservable(observable, initialValue) {
|
|
|
26
16
|
useEffect(() => {
|
|
27
17
|
initialValueRef.current = getValue(initialValue);
|
|
28
18
|
}, [initialValue]);
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
const store = useMemo(() => {
|
|
20
|
+
if (!cache.has(observable)) {
|
|
21
|
+
const entry = {
|
|
22
|
+
snapshot: initialValueRef.current,
|
|
23
|
+
};
|
|
24
|
+
entry.observable = observable.pipe(shareReplay({ refCount: true, bufferSize: 1 }), tap(value => (entry.snapshot = value)));
|
|
25
|
+
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns, and keep the observable alive until the component unmounts.
|
|
26
|
+
entry.subscription = entry.observable.subscribe();
|
|
27
|
+
cache.set(observable, entry);
|
|
33
28
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
function subscribe(callback) {
|
|
40
|
-
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here as we only need 1 subscription active to keep the observer alive
|
|
41
|
-
const sub = store.observable.subscribe(callback);
|
|
42
|
-
return () => {
|
|
43
|
-
sub.unsubscribe();
|
|
44
|
-
};
|
|
45
|
-
},
|
|
46
|
-
];
|
|
47
|
-
}, [observable]);
|
|
48
|
-
const shouldRestoreSubscriptionRef = useRef(false);
|
|
49
|
-
useEffect(() => {
|
|
50
|
-
const store = getOrCreateStore(observable, initialValueRef.current);
|
|
51
|
-
if (shouldRestoreSubscriptionRef.current) {
|
|
52
|
-
if (store.subscription.closed) {
|
|
53
|
-
store.subscription = store.observable.subscribe();
|
|
54
|
-
}
|
|
55
|
-
shouldRestoreSubscriptionRef.current = false;
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
30
|
+
const instance = cache.get(observable);
|
|
31
|
+
if (instance.subscription.closed) {
|
|
32
|
+
instance.subscription = instance.observable.subscribe();
|
|
56
33
|
}
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
34
|
+
return {
|
|
35
|
+
subscribe: (onStoreChange) => {
|
|
36
|
+
const subscription = instance.observable.subscribe(onStoreChange);
|
|
37
|
+
instance.subscription.unsubscribe();
|
|
38
|
+
return () => subscription.unsubscribe();
|
|
39
|
+
},
|
|
40
|
+
getSnapshot: () => instance.snapshot,
|
|
62
41
|
};
|
|
63
42
|
}, [observable]);
|
|
64
|
-
return useSyncExternalStore(subscribe, getSnapshot);
|
|
43
|
+
return useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
65
44
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Observable } from 'rxjs';
|
|
2
|
-
export declare function useObservable<
|
|
3
|
-
|
|
4
|
-
export
|
|
1
|
+
import type { Observable } from 'rxjs';
|
|
2
|
+
export declare function useObservable<ObservableType extends Observable<any>>(observable: ObservableType, initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>)): UnboxObservable<ObservableType>;
|
|
3
|
+
declare type UnboxObservable<T> = T extends Observable<infer U> ? U : never;
|
|
4
|
+
export {};
|
|
@@ -4,16 +4,6 @@ function getValue(value) {
|
|
|
4
4
|
return typeof value === 'function' ? value() : value;
|
|
5
5
|
}
|
|
6
6
|
var cache = new WeakMap();
|
|
7
|
-
function getOrCreateStore(inputObservable, initialValue) {
|
|
8
|
-
if (!cache.has(inputObservable)) {
|
|
9
|
-
var entry_1 = { currentValue: initialValue };
|
|
10
|
-
entry_1.observable = inputObservable.pipe(shareReplay({ refCount: true, bufferSize: 1 }), tap(function (value) { return (entry_1.currentValue = value); }));
|
|
11
|
-
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns
|
|
12
|
-
entry_1.subscription = entry_1.observable.subscribe();
|
|
13
|
-
cache.set(inputObservable, entry_1);
|
|
14
|
-
}
|
|
15
|
-
return cache.get(inputObservable);
|
|
16
|
-
}
|
|
17
7
|
export function useObservable(observable, initialValue) {
|
|
18
8
|
/**
|
|
19
9
|
* Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
|
|
@@ -26,40 +16,29 @@ export function useObservable(observable, initialValue) {
|
|
|
26
16
|
useEffect(function () {
|
|
27
17
|
initialValueRef.current = getValue(initialValue);
|
|
28
18
|
}, [initialValue]);
|
|
29
|
-
var
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
19
|
+
var store = useMemo(function () {
|
|
20
|
+
if (!cache.has(observable)) {
|
|
21
|
+
var entry_1 = {
|
|
22
|
+
snapshot: initialValueRef.current,
|
|
23
|
+
};
|
|
24
|
+
entry_1.observable = observable.pipe(shareReplay({ refCount: true, bufferSize: 1 }), tap(function (value) { return (entry_1.snapshot = value); }));
|
|
25
|
+
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns, and keep the observable alive until the component unmounts.
|
|
26
|
+
entry_1.subscription = entry_1.observable.subscribe();
|
|
27
|
+
cache.set(observable, entry_1);
|
|
33
28
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
function subscribe(callback) {
|
|
40
|
-
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here as we only need 1 subscription active to keep the observer alive
|
|
41
|
-
var sub = store.observable.subscribe(callback);
|
|
42
|
-
return function () {
|
|
43
|
-
sub.unsubscribe();
|
|
44
|
-
};
|
|
45
|
-
},
|
|
46
|
-
];
|
|
47
|
-
}, [observable]), getSnapshot = _a[0], subscribe = _a[1];
|
|
48
|
-
var shouldRestoreSubscriptionRef = useRef(false);
|
|
49
|
-
useEffect(function () {
|
|
50
|
-
var store = getOrCreateStore(observable, initialValueRef.current);
|
|
51
|
-
if (shouldRestoreSubscriptionRef.current) {
|
|
52
|
-
if (store.subscription.closed) {
|
|
53
|
-
store.subscription = store.observable.subscribe();
|
|
54
|
-
}
|
|
55
|
-
shouldRestoreSubscriptionRef.current = false;
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
30
|
+
var instance = cache.get(observable);
|
|
31
|
+
if (instance.subscription.closed) {
|
|
32
|
+
instance.subscription = instance.observable.subscribe();
|
|
56
33
|
}
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
34
|
+
return {
|
|
35
|
+
subscribe: function (onStoreChange) {
|
|
36
|
+
var subscription = instance.observable.subscribe(onStoreChange);
|
|
37
|
+
instance.subscription.unsubscribe();
|
|
38
|
+
return function () { return subscription.unsubscribe(); };
|
|
39
|
+
},
|
|
40
|
+
getSnapshot: function () { return instance.snapshot; },
|
|
62
41
|
};
|
|
63
42
|
}, [observable]);
|
|
64
|
-
return useSyncExternalStore(subscribe, getSnapshot);
|
|
43
|
+
return useSyncExternalStore(store.subscribe, store.getSnapshot);
|
|
65
44
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-rx",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-3",
|
|
4
4
|
"description": "React + RxJS = <3",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"action",
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@sanity/semantic-release-preset": "^2.0.5",
|
|
77
|
-
"@testing-library/dom": "^
|
|
78
|
-
"@testing-library/react": "^
|
|
77
|
+
"@testing-library/dom": "^10.1.0",
|
|
78
|
+
"@testing-library/react": "^16.0.0",
|
|
79
79
|
"@types/jest": "^29.5.3",
|
|
80
80
|
"@types/node": "^18.17.5",
|
|
81
81
|
"@types/react": "^18.3.3",
|
package/src/useObservable.ts
CHANGED
|
@@ -1,90 +1,67 @@
|
|
|
1
1
|
import {useEffect, useMemo, useRef, useSyncExternalStore} from 'react'
|
|
2
|
-
import {Observable, Subscription} from 'rxjs'
|
|
2
|
+
import type {Observable, Subscription} from 'rxjs'
|
|
3
3
|
import {shareReplay, tap} from 'rxjs/operators'
|
|
4
4
|
|
|
5
5
|
function getValue<T>(value: T): T extends () => infer U ? U : T {
|
|
6
6
|
return typeof value === 'function' ? value() : value
|
|
7
7
|
}
|
|
8
|
-
|
|
9
8
|
interface CacheRecord<T> {
|
|
10
9
|
subscription: Subscription
|
|
11
10
|
observable: Observable<T>
|
|
12
|
-
|
|
11
|
+
snapshot: T
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
const cache = new WeakMap<Observable<any>, CacheRecord<any>>()
|
|
16
|
-
function getOrCreateStore<T>(inputObservable: Observable<T>, initialValue: T) {
|
|
17
|
-
if (!cache.has(inputObservable)) {
|
|
18
|
-
const entry: Partial<CacheRecord<T>> = {currentValue: initialValue}
|
|
19
|
-
entry.observable = inputObservable.pipe(
|
|
20
|
-
shareReplay({refCount: true, bufferSize: 1}),
|
|
21
|
-
tap(value => (entry.currentValue = value)),
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns
|
|
25
|
-
entry.subscription = entry.observable.subscribe()
|
|
26
|
-
|
|
27
|
-
cache.set(inputObservable, entry as CacheRecord<T>)
|
|
28
|
-
}
|
|
29
|
-
return cache.get(inputObservable)!
|
|
30
|
-
}
|
|
31
15
|
|
|
32
|
-
export function useObservable<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
16
|
+
export function useObservable<ObservableType extends Observable<any>>(
|
|
17
|
+
observable: ObservableType,
|
|
18
|
+
initialValue?: UnboxObservable<ObservableType> | (() => UnboxObservable<ObservableType>),
|
|
19
|
+
): UnboxObservable<ObservableType> {
|
|
36
20
|
/**
|
|
37
21
|
* Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
|
|
38
22
|
* But we also don't want the initialValue to be stale if the observable changes.
|
|
39
23
|
*/
|
|
40
|
-
const initialValueRef = useRef(getValue(initialValue))
|
|
24
|
+
const initialValueRef = useRef(getValue(initialValue) as UnboxObservable<ObservableType>)
|
|
41
25
|
|
|
42
26
|
/**
|
|
43
27
|
* Ensures that the initialValue is always up-to-date in case the observable changes.
|
|
44
28
|
*/
|
|
45
29
|
useEffect(() => {
|
|
46
|
-
initialValueRef.current = getValue(initialValue)
|
|
30
|
+
initialValueRef.current = getValue(initialValue) as UnboxObservable<ObservableType>
|
|
47
31
|
}, [initialValue])
|
|
48
32
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
if (store.subscription.closed) {
|
|
54
|
-
store.subscription = store.observable.subscribe()
|
|
55
|
-
}
|
|
56
|
-
return [
|
|
57
|
-
function getSnapshot() {
|
|
58
|
-
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here to clear up some memory, as this subscription is only needed to provide a sync initialValue.
|
|
59
|
-
return store.currentValue
|
|
60
|
-
},
|
|
61
|
-
function subscribe(callback: () => void) {
|
|
62
|
-
// @TODO: perf opt opportunity: we could do `store.subscription.unsubscribe()` here as we only need 1 subscription active to keep the observer alive
|
|
63
|
-
const sub = store.observable.subscribe(callback)
|
|
64
|
-
return () => {
|
|
65
|
-
sub.unsubscribe()
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
]
|
|
69
|
-
}, [observable])
|
|
70
|
-
|
|
71
|
-
const shouldRestoreSubscriptionRef = useRef(false)
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
const store = getOrCreateStore(observable, initialValueRef.current)
|
|
74
|
-
if (shouldRestoreSubscriptionRef.current) {
|
|
75
|
-
if (store.subscription.closed) {
|
|
76
|
-
store.subscription = store.observable.subscribe()
|
|
33
|
+
const store = useMemo(() => {
|
|
34
|
+
if (!cache.has(observable)) {
|
|
35
|
+
const entry: Partial<CacheRecord<UnboxObservable<ObservableType>>> = {
|
|
36
|
+
snapshot: initialValueRef.current,
|
|
77
37
|
}
|
|
78
|
-
|
|
38
|
+
entry.observable = observable.pipe(
|
|
39
|
+
shareReplay({refCount: true, bufferSize: 1}),
|
|
40
|
+
tap(value => (entry.snapshot = value)),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// Eagerly subscribe to sync set `entry.currentValue` to what the observable returns, and keep the observable alive until the component unmounts.
|
|
44
|
+
entry.subscription = entry.observable.subscribe()
|
|
45
|
+
|
|
46
|
+
cache.set(observable, entry as CacheRecord<UnboxObservable<ObservableType>>)
|
|
47
|
+
}
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
49
|
+
const instance = cache.get(observable)!
|
|
50
|
+
if (instance.subscription.closed) {
|
|
51
|
+
instance.subscription = instance.observable.subscribe()
|
|
79
52
|
}
|
|
80
53
|
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
54
|
+
return {
|
|
55
|
+
subscribe: (onStoreChange: () => void) => {
|
|
56
|
+
const subscription = instance.observable.subscribe(onStoreChange)
|
|
57
|
+
instance.subscription.unsubscribe()
|
|
58
|
+
return () => subscription.unsubscribe()
|
|
59
|
+
},
|
|
60
|
+
getSnapshot: () => instance.snapshot,
|
|
86
61
|
}
|
|
87
62
|
}, [observable])
|
|
88
63
|
|
|
89
|
-
return useSyncExternalStore(subscribe, getSnapshot)
|
|
64
|
+
return useSyncExternalStore<UnboxObservable<ObservableType>>(store.subscribe, store.getSnapshot)
|
|
90
65
|
}
|
|
66
|
+
|
|
67
|
+
type UnboxObservable<T> = T extends Observable<infer U> ? U : never
|