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.
@@ -1,4 +1,4 @@
1
- import { Observable } from 'rxjs';
2
- export declare function useObservable<T>(observable: Observable<T>): T | undefined;
3
- export declare function useObservable<T>(observable: Observable<T>, initialValue: T): T;
4
- export declare function useObservable<T>(observable: Observable<T>, initialValue: () => T): T;
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 _a = (0, react_1.useMemo)(function () {
33
- var store = getOrCreateStore(observable, initialValueRef.current);
34
- if (store.subscription.closed) {
35
- store.subscription = store.observable.subscribe();
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
- return [
38
- function getSnapshot() {
39
- // @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.
40
- return store.currentValue;
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 function () {
61
- // React StrictMode will call effects as `setup + teardown + setup` thus we can't trust this callback as "react is about to unmount"
62
- // Tracking this ref lets us set the subscription back up on the next `setup` call if needed, and if it really did unmounted then all is well
63
- shouldRestoreSubscriptionRef.current = !store.subscription.closed;
64
- store.subscription.unsubscribe();
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<T>(observable: Observable<T>): T | undefined;
3
- export declare function useObservable<T>(observable: Observable<T>, initialValue: T): T;
4
- export declare function useObservable<T>(observable: Observable<T>, initialValue: () => T): T;
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 [getSnapshot, subscribe] = useMemo(() => {
30
- const store = getOrCreateStore(observable, initialValueRef.current);
31
- if (store.subscription.closed) {
32
- store.subscription = store.observable.subscribe();
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
- return [
35
- function getSnapshot() {
36
- // @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.
37
- return store.currentValue;
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
- // React StrictMode will call effects as `setup + teardown + setup` thus we can't trust this callback as "react is about to unmount"
59
- // Tracking this ref lets us set the subscription back up on the next `setup` call if needed, and if it really did unmounted then all is well
60
- shouldRestoreSubscriptionRef.current = !store.subscription.closed;
61
- store.subscription.unsubscribe();
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<T>(observable: Observable<T>): T | undefined;
3
- export declare function useObservable<T>(observable: Observable<T>, initialValue: T): T;
4
- export declare function useObservable<T>(observable: Observable<T>, initialValue: () => T): T;
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 _a = useMemo(function () {
30
- var store = getOrCreateStore(observable, initialValueRef.current);
31
- if (store.subscription.closed) {
32
- store.subscription = store.observable.subscribe();
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
- return [
35
- function getSnapshot() {
36
- // @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.
37
- return store.currentValue;
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 function () {
58
- // React StrictMode will call effects as `setup + teardown + setup` thus we can't trust this callback as "react is about to unmount"
59
- // Tracking this ref lets us set the subscription back up on the next `setup` call if needed, and if it really did unmounted then all is well
60
- shouldRestoreSubscriptionRef.current = !store.subscription.closed;
61
- store.subscription.unsubscribe();
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-1",
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": "^8.20.1",
78
- "@testing-library/react": "^13.4.0",
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",
@@ -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
- currentValue: T
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<T>(observable: Observable<T>): T | undefined
33
- export function useObservable<T>(observable: Observable<T>, initialValue: T): T
34
- export function useObservable<T>(observable: Observable<T>, initialValue: () => T): T
35
- export function useObservable<T>(observable: Observable<T>, initialValue?: T | (() => T)) {
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 [getSnapshot, subscribe] = useMemo<
50
- [() => T, Parameters<typeof useSyncExternalStore>[0]]
51
- >(() => {
52
- const store = getOrCreateStore(observable, initialValueRef.current)
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
- shouldRestoreSubscriptionRef.current = false
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
- // React StrictMode will call effects as `setup + teardown + setup` thus we can't trust this callback as "react is about to unmount"
83
- // Tracking this ref lets us set the subscription back up on the next `setup` call if needed, and if it really did unmounted then all is well
84
- shouldRestoreSubscriptionRef.current = !store.subscription.closed
85
- store.subscription.unsubscribe()
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