react-rx 3.0.0-0 → 3.0.0-1

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.
Files changed (79) hide show
  1. package/dist/cjs/__tests__/strictmode.test.js +22 -62
  2. package/dist/cjs/__tests__/useObservable.test.js +32 -33
  3. package/dist/cjs/index.d.ts +2 -7
  4. package/dist/cjs/index.js +16 -26
  5. package/dist/cjs/useObservable.d.ts +0 -3
  6. package/dist/cjs/useObservable.js +58 -20
  7. package/dist/cjs/{useObservableCallback.js → useObservableEvent.js} +1 -2
  8. package/dist/es2015/__tests__/strictmode.test.js +6 -42
  9. package/dist/es2015/__tests__/useObservable.test.js +2 -3
  10. package/dist/es2015/index.d.ts +2 -7
  11. package/dist/es2015/index.js +2 -9
  12. package/dist/es2015/useObservable.d.ts +0 -3
  13. package/dist/es2015/useObservable.js +58 -19
  14. package/dist/es2015/{useObservableCallback.js → useObservableEvent.js} +1 -2
  15. package/dist/esm/__tests__/strictmode.test.js +8 -48
  16. package/dist/esm/__tests__/useObservable.test.js +2 -3
  17. package/dist/esm/index.d.ts +2 -7
  18. package/dist/esm/index.js +2 -9
  19. package/dist/esm/useObservable.d.ts +0 -3
  20. package/dist/esm/useObservable.js +58 -19
  21. package/dist/esm/{useObservableCallback.js → useObservableEvent.js} +1 -2
  22. package/package.json +35 -28
  23. package/src/__tests__/strictmode.test.ts +7 -52
  24. package/src/__tests__/useObservable.test.tsx +3 -3
  25. package/src/index.ts +2 -15
  26. package/src/useObservable.ts +75 -33
  27. package/src/{useObservableCallback.ts → useObservableEvent.ts} +1 -2
  28. package/dist/cjs/WithObservable.d.ts +0 -12
  29. package/dist/cjs/WithObservable.js +0 -16
  30. package/dist/cjs/__tests__/useAsObservable.test.d.ts +0 -1
  31. package/dist/cjs/__tests__/useAsObservable.test.js +0 -78
  32. package/dist/cjs/displayName.d.ts +0 -1
  33. package/dist/cjs/displayName.js +0 -14
  34. package/dist/cjs/reactiveComponent.d.ts +0 -8
  35. package/dist/cjs/reactiveComponent.js +0 -35
  36. package/dist/cjs/useAsObservable.d.ts +0 -10
  37. package/dist/cjs/useAsObservable.js +0 -47
  38. package/dist/cjs/useWithObservable.d.ts +0 -9
  39. package/dist/cjs/useWithObservable.js +0 -11
  40. package/dist/cjs/utils.d.ts +0 -7
  41. package/dist/cjs/utils.js +0 -38
  42. package/dist/es2015/WithObservable.d.ts +0 -12
  43. package/dist/es2015/WithObservable.js +0 -9
  44. package/dist/es2015/__tests__/useAsObservable.test.d.ts +0 -1
  45. package/dist/es2015/__tests__/useAsObservable.test.js +0 -73
  46. package/dist/es2015/displayName.d.ts +0 -1
  47. package/dist/es2015/displayName.js +0 -8
  48. package/dist/es2015/reactiveComponent.d.ts +0 -8
  49. package/dist/es2015/reactiveComponent.js +0 -30
  50. package/dist/es2015/useAsObservable.d.ts +0 -10
  51. package/dist/es2015/useAsObservable.js +0 -43
  52. package/dist/es2015/useWithObservable.d.ts +0 -9
  53. package/dist/es2015/useWithObservable.js +0 -7
  54. package/dist/es2015/utils.d.ts +0 -7
  55. package/dist/es2015/utils.js +0 -31
  56. package/dist/esm/WithObservable.d.ts +0 -12
  57. package/dist/esm/WithObservable.js +0 -13
  58. package/dist/esm/__tests__/useAsObservable.test.d.ts +0 -1
  59. package/dist/esm/__tests__/useAsObservable.test.js +0 -73
  60. package/dist/esm/displayName.d.ts +0 -1
  61. package/dist/esm/displayName.js +0 -10
  62. package/dist/esm/reactiveComponent.d.ts +0 -8
  63. package/dist/esm/reactiveComponent.js +0 -30
  64. package/dist/esm/useAsObservable.d.ts +0 -10
  65. package/dist/esm/useAsObservable.js +0 -43
  66. package/dist/esm/useWithObservable.d.ts +0 -9
  67. package/dist/esm/useWithObservable.js +0 -7
  68. package/dist/esm/utils.d.ts +0 -7
  69. package/dist/esm/utils.js +0 -31
  70. package/src/WithObservable.tsx +0 -29
  71. package/src/__tests__/useAsObservable.test.tsx +0 -95
  72. package/src/displayName.ts +0 -11
  73. package/src/reactiveComponent.ts +0 -63
  74. package/src/useAsObservable.ts +0 -66
  75. package/src/useWithObservable.ts +0 -24
  76. package/src/utils.ts +0 -48
  77. /package/dist/cjs/{useObservableCallback.d.ts → useObservableEvent.d.ts} +0 -0
  78. /package/dist/es2015/{useObservableCallback.d.ts → useObservableEvent.d.ts} +0 -0
  79. /package/dist/esm/{useObservableCallback.d.ts → useObservableEvent.d.ts} +0 -0
@@ -1,48 +1,90 @@
1
- import {Observable} from 'rxjs'
2
- import {DependencyList, useEffect, useRef, useState} from 'react'
3
- import {tap} from 'rxjs/operators'
1
+ import {useEffect, useMemo, useRef, useSyncExternalStore} from 'react'
2
+ import {Observable, Subscription} from 'rxjs'
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
8
 
9
+ interface CacheRecord<T> {
10
+ subscription: Subscription
11
+ observable: Observable<T>
12
+ currentValue: T
13
+ }
14
+
15
+ 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
+
9
32
  export function useObservable<T>(observable: Observable<T>): T | undefined
10
33
  export function useObservable<T>(observable: Observable<T>, initialValue: T): T
11
34
  export function useObservable<T>(observable: Observable<T>, initialValue: () => T): T
12
35
  export function useObservable<T>(observable: Observable<T>, initialValue?: T | (() => T)) {
13
- const [value, setValue] = useState(() => getValue(initialValue))
14
- const initialValueRef = useRef(initialValue)
15
- initialValueRef.current = initialValue
36
+ /**
37
+ * Store the initialValue in a ref, as we don't want a changed `initialValue` to trigger a re-subscription.
38
+ * But we also don't want the initialValue to be stale if the observable changes.
39
+ */
40
+ const initialValueRef = useRef(getValue(initialValue))
41
+
42
+ /**
43
+ * Ensures that the initialValue is always up-to-date in case the observable changes.
44
+ */
16
45
  useEffect(() => {
17
- setValue(getValue(initialValueRef.current))
18
- const subscription = observable.pipe(tap(next => setValue(next))).subscribe()
19
- return () => subscription.unsubscribe()
20
- }, [initialValueRef, observable])
46
+ initialValueRef.current = getValue(initialValue)
47
+ }, [initialValue])
21
48
 
22
- return value
23
- }
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])
24
70
 
25
- export function useMemoObservable<T>(
26
- observableOrFactory: Observable<T> | (() => Observable<T>),
27
- deps: DependencyList,
28
- ): T | undefined
29
- export function useMemoObservable<T>(
30
- observableOrFactory: Observable<T> | (() => Observable<T>),
31
- deps: DependencyList,
32
- initialValue: T | (() => T),
33
- ): T
34
- export function useMemoObservable<T>(
35
- observableOrFactory: Observable<T> | (() => Observable<T>),
36
- deps: DependencyList,
37
- initialValue?: T | (() => T),
38
- ) {
39
- const [value, setValue] = useState(() => getValue(initialValue))
71
+ const shouldRestoreSubscriptionRef = useRef(false)
40
72
  useEffect(() => {
41
- const subscription = getValue(observableOrFactory)
42
- .pipe(tap(next => setValue(next)))
43
- .subscribe()
44
- return () => subscription.unsubscribe()
45
- }, deps)
73
+ const store = getOrCreateStore(observable, initialValueRef.current)
74
+ if (shouldRestoreSubscriptionRef.current) {
75
+ if (store.subscription.closed) {
76
+ store.subscription = store.observable.subscribe()
77
+ }
78
+ shouldRestoreSubscriptionRef.current = false
79
+ }
80
+
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()
86
+ }
87
+ }, [observable])
46
88
 
47
- return value
89
+ return useSyncExternalStore(subscribe, getSnapshot)
48
90
  }
@@ -1,5 +1,5 @@
1
- import {DependencyList, useCallback, useEffect, useRef} from 'react'
2
1
  import {observableCallback} from 'observable-callback'
2
+ import {DependencyList, useCallback, useEffect, useRef} from 'react'
3
3
  import {Observable} from 'rxjs'
4
4
 
5
5
  const EMPTY_DEPS: DependencyList = []
@@ -16,7 +16,6 @@ export function useObservableCallback<T, U>(
16
16
 
17
17
  const [calls$, call] = callbackRef.current
18
18
 
19
- // eslint-disable-next-line react-hooks/exhaustive-deps
20
19
  const callback = useCallback(fn, dependencies)
21
20
 
22
21
  useEffect(() => {
@@ -1,12 +0,0 @@
1
- import { Observable } from 'rxjs';
2
- import { ComponentType, ReactNode } from 'react';
3
- interface Props<T> {
4
- observable: Observable<T>;
5
- children: (value: T) => ReactNode;
6
- }
7
- type ObservableComponent<T> = ComponentType<Props<T>>;
8
- /**
9
- * @deprecated Use the useObservable hook instead
10
- */
11
- export declare const WithObservable: ObservableComponent<unknown>;
12
- export {};
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WithObservable = void 0;
4
- var operators_1 = require("rxjs/operators");
5
- var reactiveComponent_1 = require("./reactiveComponent");
6
- function createWithObservable() {
7
- return (0, reactiveComponent_1.reactiveComponent)(function (props$) {
8
- return props$.pipe((0, operators_1.distinctUntilChanged)(function (props, prevProps) { return props.observable === prevProps.observable; }), (0, operators_1.switchMap)(function (props) {
9
- return props.observable.pipe((0, operators_1.map)(function (observableValue) { return props.children(observableValue); }));
10
- }));
11
- });
12
- }
13
- /**
14
- * @deprecated Use the useObservable hook instead
15
- */
16
- exports.WithObservable = createWithObservable();
@@ -1 +0,0 @@
1
- export {};
@@ -1,78 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- var useAsObservable_1 = require("../useAsObservable");
7
- var react_1 = __importDefault(require("react"));
8
- var react_2 = require("@testing-library/react");
9
- var vitest_1 = require("vitest");
10
- (0, vitest_1.test)('the returned observable should receive a new value when component is rendered with a new value', function () {
11
- var receivedValues = [];
12
- var _a = (0, react_2.renderHook)(function (props) {
13
- var observable = (0, useAsObservable_1.useAsObservable)(props.value);
14
- react_1.default.useEffect(function () {
15
- var subscription = observable.subscribe(function (v) {
16
- receivedValues.push(v);
17
- });
18
- return function () {
19
- subscription.unsubscribe();
20
- };
21
- }, []);
22
- }, { initialProps: { value: 'initial' } }), unmount = _a.unmount, rerender = _a.rerender;
23
- rerender({ value: 'rerender' });
24
- rerender({ value: 'rerender again' });
25
- (0, vitest_1.expect)(receivedValues).toEqual(['initial', 'rerender', 'rerender again']);
26
- unmount();
27
- });
28
- (0, vitest_1.test)('the returned observable should *not* receive a new value when component is rendered with an unchanged value', function () {
29
- var receivedValues = [];
30
- var _a = (0, react_2.renderHook)(function (props) {
31
- var observable = (0, useAsObservable_1.useAsObservable)(props.value);
32
- react_1.default.useEffect(function () {
33
- var subscription = observable.subscribe(function (v) {
34
- receivedValues.push(v);
35
- });
36
- return function () {
37
- subscription.unsubscribe();
38
- };
39
- }, []);
40
- }, { initialProps: { value: 'some value' } }), unmount = _a.unmount, rerender = _a.rerender;
41
- rerender({ value: 'some value' });
42
- rerender({ value: 'some value' });
43
- (0, vitest_1.expect)(receivedValues).toEqual(['some value']);
44
- unmount();
45
- });
46
- (0, vitest_1.test)('the returned observable should have the same identity across multiple re-renders/hook calls', function () {
47
- var returnValues = [];
48
- var _a = (0, react_2.renderHook)(function () {
49
- returnValues.push((0, useAsObservable_1.useAsObservable)('render'));
50
- }), unmount = _a.unmount, rerender = _a.rerender;
51
- rerender();
52
- rerender();
53
- var initial = returnValues[0], firstRerender = returnValues[1], secondRerender = returnValues[2];
54
- (0, vitest_1.expect)(initial).toBe(firstRerender);
55
- (0, vitest_1.expect)(firstRerender).toBe(secondRerender);
56
- unmount();
57
- });
58
- (0, vitest_1.test)('the returned observable should complete on unmount', function () {
59
- var didComplete = false;
60
- var _a = (0, react_2.renderHook)(function () {
61
- var observable = (0, useAsObservable_1.useAsObservable)('render');
62
- react_1.default.useEffect(function () {
63
- var subscription = observable.subscribe({
64
- complete: function () {
65
- didComplete = true;
66
- },
67
- });
68
- return function () {
69
- subscription.unsubscribe();
70
- };
71
- }, []);
72
- }), unmount = _a.unmount, rerender = _a.rerender;
73
- (0, vitest_1.expect)(didComplete).toBe(false);
74
- rerender();
75
- (0, vitest_1.expect)(didComplete).toBe(false);
76
- unmount();
77
- (0, vitest_1.expect)(didComplete).toBe(true);
78
- });
@@ -1 +0,0 @@
1
- export declare const wrapDisplayName: (BaseComponent: Function, wrapperName: string) => string;
@@ -1,14 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.wrapDisplayName = void 0;
4
- var getDisplayName = function (Component) {
5
- if (typeof Component === 'string') {
6
- return Component;
7
- }
8
- return (Component && (Component.displayName || Component.name)) || 'Unknown';
9
- };
10
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/ban-types
11
- var wrapDisplayName = function (BaseComponent, wrapperName) {
12
- return "".concat(wrapperName, "(").concat(getDisplayName(BaseComponent), ")");
13
- };
14
- exports.wrapDisplayName = wrapDisplayName;
@@ -1,8 +0,0 @@
1
- import { Observable } from 'rxjs';
2
- import { FunctionComponent, ReactNode, Ref } from 'react';
3
- type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
4
- export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<Props>;
5
- export declare function reactiveComponent<Props>(component: Component<Props>): FunctionComponent<Props>;
6
- type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
7
- export declare function forwardRef<RefType, Props = {}>(component: ForwardRefComponent<RefType, Props>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<Props> & import("react").RefAttributes<RefType>>;
8
- export {};
@@ -1,35 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.forwardRef = exports.reactiveComponent = void 0;
4
- var displayName_1 = require("./displayName");
5
- var useAsObservable_1 = require("./useAsObservable");
6
- var useObservable_1 = require("./useObservable");
7
- var react_1 = require("react");
8
- function fromComponent(component) {
9
- var wrappedComponent = function (props) {
10
- return (0, react_1.createElement)(react_1.Fragment, null, (0, useObservable_1.useObservable)((0, react_1.useRef)(component((0, useAsObservable_1.useAsObservable)(props))).current));
11
- };
12
- wrappedComponent.displayName = (0, displayName_1.wrapDisplayName)(component, 'reactiveComponent');
13
- return wrappedComponent;
14
- }
15
- // eslint-disable-next-line @typescript-eslint/ban-types
16
- function fromObservable(input$) {
17
- return function ReactiveComponent() {
18
- return (0, react_1.createElement)(react_1.Fragment, null, (0, useObservable_1.useObservable)(input$));
19
- };
20
- }
21
- function reactiveComponent(observableOrComponent) {
22
- return typeof observableOrComponent === 'function'
23
- ? fromComponent(observableOrComponent)
24
- : fromObservable(observableOrComponent);
25
- }
26
- exports.reactiveComponent = reactiveComponent;
27
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/ban-types
28
- function forwardRef(component) {
29
- var wrappedComponent = (0, react_1.forwardRef)(function (props, ref) {
30
- return (0, react_1.createElement)(react_1.Fragment, null, (0, useObservable_1.useObservable)((0, react_1.useRef)(component((0, useAsObservable_1.useAsObservable)(props), ref)).current));
31
- });
32
- wrappedComponent.displayName = (0, displayName_1.wrapDisplayName)(component, 'reactiveComponent');
33
- return wrappedComponent;
34
- }
35
- exports.forwardRef = forwardRef;
@@ -1,10 +0,0 @@
1
- import { Observable } from 'rxjs';
2
- /**
3
- * React hook to convert any props or state value into an observable
4
- * Returns an observable representing updates to any React value (props, state or any other calculated value)
5
- * Note: the returned observable is the same instance throughout the component lifecycle
6
- * @param value
7
- * @deprecated use an `of` operator and `useMemoObservable` instead for a faster, more robust and siimpler solution
8
- */
9
- export declare function useAsObservable<T>(value: T): Observable<T>;
10
- export declare function useAsObservable<T, K>(value: T, operator: (input: Observable<T>) => Observable<K>): Observable<K>;
@@ -1,47 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useAsObservable = void 0;
4
- var rxjs_1 = require("rxjs");
5
- var react_1 = require("react");
6
- var operators_1 = require("rxjs/operators");
7
- function useAsObservable(value, operator) {
8
- var setup = (0, react_1.useCallback)(function () {
9
- var subject = new rxjs_1.BehaviorSubject(value);
10
- var observable = subject.asObservable().pipe((0, operators_1.distinctUntilChanged)());
11
- return [operator ? observable.pipe(operator) : observable, subject];
12
- }, []);
13
- var ref = (0, react_1.useRef)();
14
- if (!ref.current) {
15
- ref.current = setup();
16
- }
17
- var observable = ref.current[0];
18
- (0, react_1.useEffect)(function () {
19
- if (!ref.current) {
20
- return;
21
- }
22
- var _a = ref.current, subject = _a[1];
23
- subject.next(value);
24
- }, [value, ref]);
25
- var shouldRestoreSubscriptionRef = (0, react_1.useRef)(false);
26
- (0, react_1.useEffect)(function () {
27
- if (shouldRestoreSubscriptionRef.current) {
28
- if (!ref.current) {
29
- ref.current = setup();
30
- }
31
- shouldRestoreSubscriptionRef.current = false;
32
- }
33
- return function () {
34
- if (!ref.current) {
35
- return;
36
- }
37
- // React StrictMode will call effects as `setup + teardown + setup` thus we can't trust this callback as "react is about to unmount"
38
- // 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
39
- shouldRestoreSubscriptionRef.current = true;
40
- var _a = ref.current, subject = _a[1];
41
- subject.complete();
42
- ref.current = undefined;
43
- };
44
- }, []);
45
- return observable;
46
- }
47
- exports.useAsObservable = useAsObservable;
@@ -1,9 +0,0 @@
1
- import { Observable } from 'rxjs';
2
- /**
3
- * React hook to convert any props or state value into an observable
4
- * Returns an observable representing updates to any React value (props, state or any other calculated value)
5
- * Note: the returned observable is the same instance throughout the component lifecycle
6
- * @param value
7
- */
8
- export declare function useWithObservable<T>(value: T): T | undefined;
9
- export declare function useWithObservable<T, K>(value: T, operator: (input: Observable<T>) => Observable<K>): K | undefined;
@@ -1,11 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useWithObservable = void 0;
4
- var useObservable_1 = require("./useObservable");
5
- var useAsObservable_1 = require("./useAsObservable");
6
- function useWithObservable(value, operator) {
7
- return operator
8
- ? (0, useObservable_1.useObservable)((0, useAsObservable_1.useAsObservable)(value, operator))
9
- : (0, useObservable_1.useObservable)((0, useAsObservable_1.useAsObservable)(value));
10
- }
11
- exports.useWithObservable = useWithObservable;
@@ -1,7 +0,0 @@
1
- import { Observable, OperatorFunction } from 'rxjs';
2
- import { Dispatch, SetStateAction, Context } from 'react';
3
- export declare function observeState<T>(initial: T | (() => T)): [Observable<T>, Dispatch<SetStateAction<T>>];
4
- export declare function observeCallback<T>(): [Observable<T>, (arg: T) => void];
5
- export declare function observeCallback<T, K>(operator: OperatorFunction<T, K>): [Observable<K>, (arg: T) => void];
6
- export declare function observeContext<T>(context: Context<T>): Observable<T>;
7
- export declare function observeElement<T>(): [Observable<T | null>, (el: T | null) => void];
package/dist/cjs/utils.js DELETED
@@ -1,38 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.observeElement = exports.observeContext = exports.observeCallback = exports.observeState = void 0;
4
- var useAsObservable_1 = require("./useAsObservable");
5
- var observable_callback_1 = require("observable-callback");
6
- var operators_1 = require("rxjs/operators");
7
- var react_1 = require("react");
8
- var createState = function (initialState) { return (0, observable_callback_1.observableCallback)((0, operators_1.startWith)(initialState)); };
9
- function observeState(initial) {
10
- // eslint-disable-next-line react-hooks/rules-of-hooks
11
- var _a = (0, react_1.useState)(initial), value = _a[0], update = _a[1];
12
- // eslint-disable-next-line react-hooks/rules-of-hooks
13
- return [(0, useAsObservable_1.useAsObservable)(value), update];
14
- }
15
- exports.observeState = observeState;
16
- function observeCallback(operator) {
17
- // eslint-disable-next-line react-hooks/rules-of-hooks
18
- var ref = (0, react_1.useRef)();
19
- if (!ref.current) {
20
- ref.current = operator ? (0, observable_callback_1.observableCallback)(operator) : (0, observable_callback_1.observableCallback)();
21
- }
22
- return ref.current;
23
- }
24
- exports.observeCallback = observeCallback;
25
- function observeContext(context) {
26
- // eslint-disable-next-line react-hooks/rules-of-hooks
27
- return (0, useAsObservable_1.useAsObservable)((0, react_1.useContext)(context));
28
- }
29
- exports.observeContext = observeContext;
30
- function observeElement() {
31
- // eslint-disable-next-line react-hooks/rules-of-hooks
32
- var ref = (0, react_1.useRef)();
33
- if (!ref.current) {
34
- ref.current = createState(null);
35
- }
36
- return ref.current;
37
- }
38
- exports.observeElement = observeElement;
@@ -1,12 +0,0 @@
1
- import { Observable } from 'rxjs';
2
- import { ComponentType, ReactNode } from 'react';
3
- interface Props<T> {
4
- observable: Observable<T>;
5
- children: (value: T) => ReactNode;
6
- }
7
- type ObservableComponent<T> = ComponentType<Props<T>>;
8
- /**
9
- * @deprecated Use the useObservable hook instead
10
- */
11
- export declare const WithObservable: ObservableComponent<unknown>;
12
- export {};
@@ -1,9 +0,0 @@
1
- import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';
2
- import { reactiveComponent } from './reactiveComponent';
3
- function createWithObservable() {
4
- return reactiveComponent((props$) => props$.pipe(distinctUntilChanged((props, prevProps) => props.observable === prevProps.observable), switchMap((props) => props.observable.pipe(map((observableValue) => props.children(observableValue))))));
5
- }
6
- /**
7
- * @deprecated Use the useObservable hook instead
8
- */
9
- export const WithObservable = createWithObservable();
@@ -1 +0,0 @@
1
- export {};
@@ -1,73 +0,0 @@
1
- import { useAsObservable } from '../useAsObservable';
2
- import React from 'react';
3
- import { renderHook } from '@testing-library/react';
4
- import { test, expect } from 'vitest';
5
- test('the returned observable should receive a new value when component is rendered with a new value', () => {
6
- const receivedValues = [];
7
- const { unmount, rerender } = renderHook((props) => {
8
- const observable = useAsObservable(props.value);
9
- React.useEffect(() => {
10
- const subscription = observable.subscribe(v => {
11
- receivedValues.push(v);
12
- });
13
- return () => {
14
- subscription.unsubscribe();
15
- };
16
- }, []);
17
- }, { initialProps: { value: 'initial' } });
18
- rerender({ value: 'rerender' });
19
- rerender({ value: 'rerender again' });
20
- expect(receivedValues).toEqual(['initial', 'rerender', 'rerender again']);
21
- unmount();
22
- });
23
- test('the returned observable should *not* receive a new value when component is rendered with an unchanged value', () => {
24
- const receivedValues = [];
25
- const { unmount, rerender } = renderHook((props) => {
26
- const observable = useAsObservable(props.value);
27
- React.useEffect(() => {
28
- const subscription = observable.subscribe(v => {
29
- receivedValues.push(v);
30
- });
31
- return () => {
32
- subscription.unsubscribe();
33
- };
34
- }, []);
35
- }, { initialProps: { value: 'some value' } });
36
- rerender({ value: 'some value' });
37
- rerender({ value: 'some value' });
38
- expect(receivedValues).toEqual(['some value']);
39
- unmount();
40
- });
41
- test('the returned observable should have the same identity across multiple re-renders/hook calls', () => {
42
- const returnValues = [];
43
- const { unmount, rerender } = renderHook(() => {
44
- returnValues.push(useAsObservable('render'));
45
- });
46
- rerender();
47
- rerender();
48
- const [initial, firstRerender, secondRerender] = returnValues;
49
- expect(initial).toBe(firstRerender);
50
- expect(firstRerender).toBe(secondRerender);
51
- unmount();
52
- });
53
- test('the returned observable should complete on unmount', () => {
54
- let didComplete = false;
55
- const { unmount, rerender } = renderHook(() => {
56
- const observable = useAsObservable('render');
57
- React.useEffect(() => {
58
- const subscription = observable.subscribe({
59
- complete: () => {
60
- didComplete = true;
61
- },
62
- });
63
- return () => {
64
- subscription.unsubscribe();
65
- };
66
- }, []);
67
- });
68
- expect(didComplete).toBe(false);
69
- rerender();
70
- expect(didComplete).toBe(false);
71
- unmount();
72
- expect(didComplete).toBe(true);
73
- });
@@ -1 +0,0 @@
1
- export declare const wrapDisplayName: (BaseComponent: Function, wrapperName: string) => string;
@@ -1,8 +0,0 @@
1
- const getDisplayName = (Component) => {
2
- if (typeof Component === 'string') {
3
- return Component;
4
- }
5
- return (Component && (Component.displayName || Component.name)) || 'Unknown';
6
- };
7
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/ban-types
8
- export const wrapDisplayName = (BaseComponent, wrapperName) => `${wrapperName}(${getDisplayName(BaseComponent)})`;
@@ -1,8 +0,0 @@
1
- import { Observable } from 'rxjs';
2
- import { FunctionComponent, ReactNode, Ref } from 'react';
3
- type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
4
- export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<Props>;
5
- export declare function reactiveComponent<Props>(component: Component<Props>): FunctionComponent<Props>;
6
- type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
7
- export declare function forwardRef<RefType, Props = {}>(component: ForwardRefComponent<RefType, Props>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<Props> & import("react").RefAttributes<RefType>>;
8
- export {};
@@ -1,30 +0,0 @@
1
- import { wrapDisplayName } from './displayName';
2
- import { useAsObservable } from './useAsObservable';
3
- import { useObservable } from './useObservable';
4
- import { createElement, Fragment, useRef, forwardRef as reactForwardRef, } from 'react';
5
- function fromComponent(component) {
6
- const wrappedComponent = (props) => {
7
- return createElement(Fragment, null, useObservable(useRef(component(useAsObservable(props))).current));
8
- };
9
- wrappedComponent.displayName = wrapDisplayName(component, 'reactiveComponent');
10
- return wrappedComponent;
11
- }
12
- // eslint-disable-next-line @typescript-eslint/ban-types
13
- function fromObservable(input$) {
14
- return function ReactiveComponent() {
15
- return createElement(Fragment, null, useObservable(input$));
16
- };
17
- }
18
- export function reactiveComponent(observableOrComponent) {
19
- return typeof observableOrComponent === 'function'
20
- ? fromComponent(observableOrComponent)
21
- : fromObservable(observableOrComponent);
22
- }
23
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/ban-types
24
- export function forwardRef(component) {
25
- const wrappedComponent = reactForwardRef((props, ref) => {
26
- return createElement(Fragment, null, useObservable(useRef(component(useAsObservable(props), ref)).current));
27
- });
28
- wrappedComponent.displayName = wrapDisplayName(component, 'reactiveComponent');
29
- return wrappedComponent;
30
- }
@@ -1,10 +0,0 @@
1
- import { Observable } from 'rxjs';
2
- /**
3
- * React hook to convert any props or state value into an observable
4
- * Returns an observable representing updates to any React value (props, state or any other calculated value)
5
- * Note: the returned observable is the same instance throughout the component lifecycle
6
- * @param value
7
- * @deprecated use an `of` operator and `useMemoObservable` instead for a faster, more robust and siimpler solution
8
- */
9
- export declare function useAsObservable<T>(value: T): Observable<T>;
10
- export declare function useAsObservable<T, K>(value: T, operator: (input: Observable<T>) => Observable<K>): Observable<K>;