react-rx 2.0.1 → 2.0.4

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/.idea/vcs.xml CHANGED
@@ -1,9 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
2
  <project version="4">
3
3
  <component name="VcsDirectoryMappings">
4
- <mapping directory="$PROJECT_DIR$/../../_/parcel-transformer-mdx-with-toc" vcs="Git" />
5
- <mapping directory="$PROJECT_DIR$/../react-repl" vcs="Git" />
6
4
  <mapping directory="$PROJECT_DIR$" vcs="Git" />
7
- <mapping directory="$PROJECT_DIR$/../react-rx.dev" vcs="Git" />
8
5
  </component>
9
6
  </project>
@@ -1,7 +1,7 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { FunctionComponent, ReactNode, Ref } from 'react';
3
3
  declare type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
4
- export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<{}>;
4
+ export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<Props>;
5
5
  export declare function reactiveComponent<Props>(component: Component<Props>): FunctionComponent<Props>;
6
6
  declare type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
7
7
  export declare function forwardRef<RefType, Props = {}>(component: ForwardRefComponent<RefType, Props>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<Props> & import("react").RefAttributes<RefType>>;
@@ -4,18 +4,33 @@ exports.useAsObservable = void 0;
4
4
  var rxjs_1 = require("rxjs");
5
5
  var react_1 = require("react");
6
6
  var useIsomorphicEffect_1 = require("./useIsomorphicEffect");
7
+ var operators_1 = require("rxjs/operators");
7
8
  function useAsObservable(value, operator) {
8
- var _a = (0, react_1.useMemo)(function () {
9
+ var setup = (0, react_1.useCallback)(function () {
9
10
  var subject = new rxjs_1.BehaviorSubject(value);
10
- var observable = subject.asObservable();
11
+ var observable = subject.asObservable().pipe((0, operators_1.distinctUntilChanged)());
11
12
  return [operator ? observable.pipe(operator) : observable, subject];
12
- }, []), observable = _a[0], subject = _a[1];
13
+ }, []);
14
+ var ref = (0, react_1.useRef)();
15
+ if (!ref.current) {
16
+ ref.current = setup();
17
+ }
18
+ var observable = ref.current[0];
13
19
  (0, useIsomorphicEffect_1.useIsomorphicEffect)(function () {
20
+ if (!ref.current) {
21
+ return;
22
+ }
23
+ var _a = ref.current, subject = _a[1];
14
24
  subject.next(value);
15
- }, [value]);
16
- (0, react_1.useEffect)(function () {
25
+ }, [value, ref]);
26
+ (0, useIsomorphicEffect_1.useIsomorphicEffect)(function () {
17
27
  return function () {
28
+ if (!ref.current) {
29
+ return;
30
+ }
31
+ var _a = ref.current, subject = _a[1];
18
32
  subject.complete();
33
+ ref.current = undefined;
19
34
  };
20
35
  }, []);
21
36
  return observable;
@@ -1,37 +1,44 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useMemoObservable = exports.useObservable = void 0;
4
- var rxjs_1 = require("rxjs");
5
4
  var react_1 = require("react");
6
- var useAsObservable_1 = require("./useAsObservable");
7
- var operators_1 = require("rxjs/operators");
8
5
  var shim_1 = require("use-sync-external-store/shim");
6
+ var operators_1 = require("rxjs/operators");
7
+ var useIsomorphicEffect_1 = require("./useIsomorphicEffect");
9
8
  function getValue(value) {
10
9
  return typeof value === 'function' ? value() : value;
11
10
  }
12
- function useObservableSubscription(observable) {
13
- var store = (0, react_1.useMemo)(function () {
14
- var currentValue;
15
- return {
16
- // Note: this works because the given observable always emits a value synchronously by concat-ing the given initialValue
17
- getCurrentValue: function () { return currentValue; },
18
- subscribe: function (callback) {
19
- var subscription = observable.subscribe(function (value) {
20
- currentValue = value;
21
- callback(value);
22
- });
11
+ var cache = new WeakMap();
12
+ function getOrCreateStore(inputObservable, initialValue) {
13
+ if (!cache.has(inputObservable)) {
14
+ var entry_1 = { currentValue: initialValue };
15
+ entry_1.observable = inputObservable.pipe((0, operators_1.shareReplay)({ refCount: true, bufferSize: 1 }), (0, operators_1.tap)(function (value) { return (entry_1.currentValue = value); }));
16
+ entry_1.subscription = entry_1.observable.subscribe();
17
+ cache.set(inputObservable, entry_1);
18
+ }
19
+ return cache.get(inputObservable);
20
+ }
21
+ function useObservable(observable, initialValue) {
22
+ var _a = (0, react_1.useMemo)(function () {
23
+ var record = getOrCreateStore(observable, getValue(initialValue));
24
+ return [
25
+ function getSnapshot() {
26
+ return record.currentValue;
27
+ },
28
+ function subscribe(callback) {
29
+ var sub = record.observable.subscribe(function (next) { return callback(next); });
23
30
  return function () {
24
- subscription.unsubscribe();
31
+ sub.unsubscribe();
25
32
  };
26
33
  },
34
+ ];
35
+ }, [observable]), getSnapshot = _a[0], subscribe = _a[1];
36
+ (0, useIsomorphicEffect_1.useIsomorphicEffect)(function () {
37
+ return function () {
38
+ getOrCreateStore(observable, getValue(initialValue)).subscription.unsubscribe();
27
39
  };
28
40
  }, [observable]);
29
- return (0, shim_1.useSyncExternalStore)(store.subscribe, store.getCurrentValue);
30
- }
31
- function useObservable(observable, initialValue) {
32
- return useObservableSubscription((0, useAsObservable_1.useAsObservable)(observable, (0, rxjs_1.pipe)((0, operators_1.distinctUntilChanged)(), (0, operators_1.switchMap)(function (observable) {
33
- return (0, rxjs_1.merge)((0, rxjs_1.defer)(function () { return (0, rxjs_1.of)(getValue(initialValue)); }), observable);
34
- }))));
41
+ return (0, shim_1.useSyncExternalStore)(subscribe, getSnapshot);
35
42
  }
36
43
  exports.useObservable = useObservable;
37
44
  function useMemoObservable(observableOrFactory, deps, initialValue) {
@@ -1,7 +1,7 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { FunctionComponent, ReactNode, Ref } from 'react';
3
3
  declare type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
4
- export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<{}>;
4
+ export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<Props>;
5
5
  export declare function reactiveComponent<Props>(component: Component<Props>): FunctionComponent<Props>;
6
6
  declare type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
7
7
  export declare function forwardRef<RefType, Props = {}>(component: ForwardRefComponent<RefType, Props>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<Props> & import("react").RefAttributes<RefType>>;
@@ -1,18 +1,33 @@
1
1
  import { BehaviorSubject } from 'rxjs';
2
- import { useEffect, useMemo } from 'react';
2
+ import { useCallback, useRef } from 'react';
3
3
  import { useIsomorphicEffect } from './useIsomorphicEffect';
4
+ import { distinctUntilChanged } from 'rxjs/operators';
4
5
  export function useAsObservable(value, operator) {
5
- const [observable, subject] = useMemo(() => {
6
+ const setup = useCallback(() => {
6
7
  const subject = new BehaviorSubject(value);
7
- const observable = subject.asObservable();
8
+ const observable = subject.asObservable().pipe(distinctUntilChanged());
8
9
  return [operator ? observable.pipe(operator) : observable, subject];
9
10
  }, []);
11
+ const ref = useRef();
12
+ if (!ref.current) {
13
+ ref.current = setup();
14
+ }
15
+ const [observable] = ref.current;
10
16
  useIsomorphicEffect(() => {
17
+ if (!ref.current) {
18
+ return;
19
+ }
20
+ const [, subject] = ref.current;
11
21
  subject.next(value);
12
- }, [value]);
13
- useEffect(() => {
22
+ }, [value, ref]);
23
+ useIsomorphicEffect(() => {
14
24
  return () => {
25
+ if (!ref.current) {
26
+ return;
27
+ }
28
+ const [, subject] = ref.current;
15
29
  subject.complete();
30
+ ref.current = undefined;
16
31
  };
17
32
  }, []);
18
33
  return observable;
@@ -1,32 +1,41 @@
1
- import { defer, merge, of, pipe } from 'rxjs';
2
1
  import { useMemo } from 'react';
3
- import { useAsObservable } from './useAsObservable';
4
- import { distinctUntilChanged, switchMap } from 'rxjs/operators';
5
2
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
3
+ import { shareReplay, tap } from 'rxjs/operators';
4
+ import { useIsomorphicEffect } from './useIsomorphicEffect';
6
5
  function getValue(value) {
7
6
  return typeof value === 'function' ? value() : value;
8
7
  }
9
- function useObservableSubscription(observable) {
10
- const store = useMemo(() => {
11
- let currentValue;
12
- return {
13
- // Note: this works because the given observable always emits a value synchronously by concat-ing the given initialValue
14
- getCurrentValue: () => currentValue,
15
- subscribe: (callback) => {
16
- const subscription = observable.subscribe(value => {
17
- currentValue = value;
18
- callback(value);
19
- });
8
+ const cache = new WeakMap();
9
+ function getOrCreateStore(inputObservable, initialValue) {
10
+ if (!cache.has(inputObservable)) {
11
+ const entry = { currentValue: initialValue };
12
+ entry.observable = inputObservable.pipe(shareReplay({ refCount: true, bufferSize: 1 }), tap(value => (entry.currentValue = value)));
13
+ entry.subscription = entry.observable.subscribe();
14
+ cache.set(inputObservable, entry);
15
+ }
16
+ return cache.get(inputObservable);
17
+ }
18
+ export function useObservable(observable, initialValue) {
19
+ const [getSnapshot, subscribe] = useMemo(() => {
20
+ const record = getOrCreateStore(observable, getValue(initialValue));
21
+ return [
22
+ function getSnapshot() {
23
+ return record.currentValue;
24
+ },
25
+ function subscribe(callback) {
26
+ const sub = record.observable.subscribe(next => callback(next));
20
27
  return () => {
21
- subscription.unsubscribe();
28
+ sub.unsubscribe();
22
29
  };
23
30
  },
31
+ ];
32
+ }, [observable]);
33
+ useIsomorphicEffect(() => {
34
+ return () => {
35
+ getOrCreateStore(observable, getValue(initialValue)).subscription.unsubscribe();
24
36
  };
25
37
  }, [observable]);
26
- return useSyncExternalStore(store.subscribe, store.getCurrentValue);
27
- }
28
- export function useObservable(observable, initialValue) {
29
- return useObservableSubscription(useAsObservable(observable, pipe(distinctUntilChanged(), switchMap(observable => merge(defer(() => of(getValue(initialValue))), observable)))));
38
+ return useSyncExternalStore(subscribe, getSnapshot);
30
39
  }
31
40
  export function useMemoObservable(observableOrFactory, deps, initialValue) {
32
41
  return useObservable(useMemo(() => getValue(observableOrFactory), deps), initialValue);
@@ -1,7 +1,7 @@
1
1
  import { Observable } from 'rxjs';
2
2
  import { FunctionComponent, ReactNode, Ref } from 'react';
3
3
  declare type Component<Props> = (input$: Observable<Props>) => Observable<ReactNode>;
4
- export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<{}>;
4
+ export declare function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<Props>;
5
5
  export declare function reactiveComponent<Props>(component: Component<Props>): FunctionComponent<Props>;
6
6
  declare type ForwardRefComponent<RefType, Props> = (input$: Observable<Props>, ref: Ref<RefType>) => Observable<ReactNode>;
7
7
  export declare function forwardRef<RefType, Props = {}>(component: ForwardRefComponent<RefType, Props>): import("react").ForwardRefExoticComponent<import("react").PropsWithoutRef<Props> & import("react").RefAttributes<RefType>>;
@@ -1,18 +1,33 @@
1
1
  import { BehaviorSubject } from 'rxjs';
2
- import { useEffect, useMemo } from 'react';
2
+ import { useCallback, useRef } from 'react';
3
3
  import { useIsomorphicEffect } from './useIsomorphicEffect';
4
+ import { distinctUntilChanged } from 'rxjs/operators';
4
5
  export function useAsObservable(value, operator) {
5
- var _a = useMemo(function () {
6
+ var setup = useCallback(function () {
6
7
  var subject = new BehaviorSubject(value);
7
- var observable = subject.asObservable();
8
+ var observable = subject.asObservable().pipe(distinctUntilChanged());
8
9
  return [operator ? observable.pipe(operator) : observable, subject];
9
- }, []), observable = _a[0], subject = _a[1];
10
+ }, []);
11
+ var ref = useRef();
12
+ if (!ref.current) {
13
+ ref.current = setup();
14
+ }
15
+ var observable = ref.current[0];
10
16
  useIsomorphicEffect(function () {
17
+ if (!ref.current) {
18
+ return;
19
+ }
20
+ var _a = ref.current, subject = _a[1];
11
21
  subject.next(value);
12
- }, [value]);
13
- useEffect(function () {
22
+ }, [value, ref]);
23
+ useIsomorphicEffect(function () {
14
24
  return function () {
25
+ if (!ref.current) {
26
+ return;
27
+ }
28
+ var _a = ref.current, subject = _a[1];
15
29
  subject.complete();
30
+ ref.current = undefined;
16
31
  };
17
32
  }, []);
18
33
  return observable;
@@ -1,34 +1,41 @@
1
- import { defer, merge, of, pipe } from 'rxjs';
2
1
  import { useMemo } from 'react';
3
- import { useAsObservable } from './useAsObservable';
4
- import { distinctUntilChanged, switchMap } from 'rxjs/operators';
5
2
  import { useSyncExternalStore } from 'use-sync-external-store/shim';
3
+ import { shareReplay, tap } from 'rxjs/operators';
4
+ import { useIsomorphicEffect } from './useIsomorphicEffect';
6
5
  function getValue(value) {
7
6
  return typeof value === 'function' ? value() : value;
8
7
  }
9
- function useObservableSubscription(observable) {
10
- var store = useMemo(function () {
11
- var currentValue;
12
- return {
13
- // Note: this works because the given observable always emits a value synchronously by concat-ing the given initialValue
14
- getCurrentValue: function () { return currentValue; },
15
- subscribe: function (callback) {
16
- var subscription = observable.subscribe(function (value) {
17
- currentValue = value;
18
- callback(value);
19
- });
8
+ var cache = new WeakMap();
9
+ function getOrCreateStore(inputObservable, initialValue) {
10
+ if (!cache.has(inputObservable)) {
11
+ var entry_1 = { currentValue: initialValue };
12
+ entry_1.observable = inputObservable.pipe(shareReplay({ refCount: true, bufferSize: 1 }), tap(function (value) { return (entry_1.currentValue = value); }));
13
+ entry_1.subscription = entry_1.observable.subscribe();
14
+ cache.set(inputObservable, entry_1);
15
+ }
16
+ return cache.get(inputObservable);
17
+ }
18
+ export function useObservable(observable, initialValue) {
19
+ var _a = useMemo(function () {
20
+ var record = getOrCreateStore(observable, getValue(initialValue));
21
+ return [
22
+ function getSnapshot() {
23
+ return record.currentValue;
24
+ },
25
+ function subscribe(callback) {
26
+ var sub = record.observable.subscribe(function (next) { return callback(next); });
20
27
  return function () {
21
- subscription.unsubscribe();
28
+ sub.unsubscribe();
22
29
  };
23
30
  },
31
+ ];
32
+ }, [observable]), getSnapshot = _a[0], subscribe = _a[1];
33
+ useIsomorphicEffect(function () {
34
+ return function () {
35
+ getOrCreateStore(observable, getValue(initialValue)).subscription.unsubscribe();
24
36
  };
25
37
  }, [observable]);
26
- return useSyncExternalStore(store.subscribe, store.getCurrentValue);
27
- }
28
- export function useObservable(observable, initialValue) {
29
- return useObservableSubscription(useAsObservable(observable, pipe(distinctUntilChanged(), switchMap(function (observable) {
30
- return merge(defer(function () { return of(getValue(initialValue)); }), observable);
31
- }))));
38
+ return useSyncExternalStore(subscribe, getSnapshot);
32
39
  }
33
40
  export function useMemoObservable(observableOrFactory, deps, initialValue) {
34
41
  return useObservable(useMemo(function () { return getValue(observableOrFactory); }, deps), initialValue);
package/jest.config.ts ADDED
@@ -0,0 +1,14 @@
1
+ export default {
2
+ preset: 'ts-jest',
3
+ // verbose: true,
4
+ testEnvironment: 'jsdom',
5
+ testEnvironmentOptions: {
6
+ url: 'http://localhost/',
7
+ },
8
+ globals: {
9
+ 'ts-jest': {
10
+ warnOnly: true,
11
+ },
12
+ },
13
+ rootDir: 'src',
14
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-rx",
3
- "version": "2.0.1",
3
+ "version": "2.0.4",
4
4
  "description": "React + RxJS = <3",
5
5
  "main": "dist/cjs/index.js",
6
6
  "types": "dist/cjs/index.d.ts",
@@ -30,12 +30,11 @@
30
30
  "rxjs": "^6.5 || ^7"
31
31
  },
32
32
  "devDependencies": {
33
- "@testing-library/dom": "^7.29.6",
34
- "@testing-library/react": "^11.2.5",
35
- "@testing-library/react-hooks": "^5.0.3",
36
- "@types/jest": "^26.0.20",
37
- "@types/react": "^17.0.2",
38
- "@types/react-dom": "^17.0.1",
33
+ "@testing-library/dom": "^8.14.0",
34
+ "@testing-library/react": "^13.3.0",
35
+ "@types/jest": "^28.1.4",
36
+ "@types/react": "^18.0.12",
37
+ "@types/react-dom": "^18.0.5",
39
38
  "@types/use-sync-external-store": "^0.0.3",
40
39
  "@typescript-eslint/eslint-plugin": "^4.15.2",
41
40
  "@typescript-eslint/parser": "^4.15.2",
@@ -43,16 +42,17 @@
43
42
  "eslint-config-prettier": "^8.1.0",
44
43
  "eslint-plugin-prettier": "^3.3.1",
45
44
  "eslint-plugin-react": "^7.22.0",
46
- "jest": "^26.6.3",
47
- "jsdom": "^16.4.0",
45
+ "jest": "^28.1.2",
46
+ "jest-environment-jsdom": "^28.1.2",
47
+ "jsdom": "^20.0.0",
48
48
  "npm-run-all": "^4.1.5",
49
49
  "prettier": "2.2.1",
50
- "react": "^17.0.1",
51
- "react-dom": "^17.0.1",
52
- "react-test-renderer": "^17.0.1",
50
+ "react": "^18.2.0",
51
+ "react-dom": "^18.2.0",
52
+ "react-test-renderer": "^18.2.0",
53
53
  "rimraf": "^3.0.2",
54
54
  "rxjs": "^6.5.5",
55
- "ts-jest": "^26.5.2",
55
+ "ts-jest": "^28.0.5",
56
56
  "typescript": "^4.2.2"
57
57
  },
58
58
  "repository": {
@@ -26,16 +26,16 @@ function fromComponent<Props>(component: Component<Props>): FunctionComponent<Pr
26
26
  return wrappedComponent
27
27
  }
28
28
 
29
- function fromObservable<Props>(input$: Observable<Props>): FunctionComponent<{}> {
29
+ function fromObservable<Props>(input$: Observable<ReactNode>): FunctionComponent<{}> {
30
30
  return function ReactiveComponent() {
31
31
  return createElement(Fragment, null, useObservable<ReactNode>(input$))
32
32
  }
33
33
  }
34
34
 
35
- export function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<{}>
35
+ export function reactiveComponent<Props>(observable: Observable<Props>): FunctionComponent<Props>
36
36
  export function reactiveComponent<Props>(component: Component<Props>): FunctionComponent<Props>
37
37
  export function reactiveComponent<Props>(
38
- observableOrComponent: Observable<Props> | Component<Props>,
38
+ observableOrComponent: Observable<ReactNode> | Component<Props>,
39
39
  ) {
40
40
  return typeof observableOrComponent === 'function'
41
41
  ? fromComponent(observableOrComponent)
@@ -1,6 +1,7 @@
1
1
  import {BehaviorSubject, Observable} from 'rxjs'
2
- import {useEffect, useMemo} from 'react'
2
+ import {useCallback, useRef} from 'react'
3
3
  import {useIsomorphicEffect} from './useIsomorphicEffect'
4
+ import {distinctUntilChanged} from 'rxjs/operators'
4
5
 
5
6
  /**
6
7
  * React hook to convert any props or state value into an observable
@@ -17,22 +18,38 @@ export function useAsObservable<T, K = T>(
17
18
  value: T,
18
19
  operator?: (input: Observable<T>) => Observable<K>,
19
20
  ): Observable<T | K> {
20
- const [observable, subject] = useMemo(() => {
21
+ const setup = useCallback((): [Observable<T | K>, BehaviorSubject<T>] => {
21
22
  const subject = new BehaviorSubject(value)
22
23
 
23
- const observable = subject.asObservable()
24
+ const observable = subject.asObservable().pipe(distinctUntilChanged())
24
25
  return [operator ? observable.pipe(operator) : observable, subject]
25
26
  }, [])
26
27
 
28
+ const ref = useRef<[Observable<T | K>, BehaviorSubject<T>]>()
29
+
30
+ if (!ref.current) {
31
+ ref.current = setup()
32
+ }
33
+
34
+ const [observable] = ref.current
35
+
27
36
  useIsomorphicEffect(() => {
37
+ if (!ref.current) {
38
+ return
39
+ }
40
+ const [, subject] = ref.current
28
41
  subject.next(value)
29
- }, [value])
42
+ }, [value, ref])
30
43
 
31
- useEffect(() => {
44
+ useIsomorphicEffect(() => {
32
45
  return () => {
46
+ if (!ref.current) {
47
+ return
48
+ }
49
+ const [, subject] = ref.current
33
50
  subject.complete()
51
+ ref.current = undefined
34
52
  }
35
53
  }, [])
36
-
37
54
  return observable
38
55
  }
@@ -1,51 +1,60 @@
1
- import {defer, merge, Observable, of, pipe} from 'rxjs'
1
+ import {Observable, Subscription} from 'rxjs'
2
2
  import {DependencyList, useMemo} from 'react'
3
- import {useAsObservable} from './useAsObservable'
4
- import {distinctUntilChanged, switchMap} from 'rxjs/operators'
5
3
  import {useSyncExternalStore} from 'use-sync-external-store/shim'
4
+ import {shareReplay, tap} from 'rxjs/operators'
5
+ import {useIsomorphicEffect} from './useIsomorphicEffect'
6
6
 
7
7
  function getValue<T>(value: T): T extends () => infer U ? U : T {
8
8
  return typeof value === 'function' ? value() : value
9
9
  }
10
10
 
11
- function useObservableSubscription<T>(observable: Observable<T>): T {
12
- const store = useMemo(() => {
13
- let currentValue: T
14
- return {
15
- // Note: this works because the given observable always emits a value synchronously by concat-ing the given initialValue
16
- getCurrentValue: () => currentValue,
17
- subscribe: (callback: (value: T) => void) => {
18
- const subscription = observable.subscribe(value => {
19
- currentValue = value
20
- callback(value)
21
- })
22
- return () => {
23
- subscription.unsubscribe()
24
- }
25
- },
26
- }
27
- }, [observable])
28
- return useSyncExternalStore(store.subscribe, store.getCurrentValue)
11
+ interface CacheRecord<T> {
12
+ subscription: Subscription
13
+ observable: Observable<T>
14
+ currentValue: T
15
+ }
16
+
17
+ const cache = new WeakMap<Observable<any>, CacheRecord<any>>()
18
+ function getOrCreateStore<T>(inputObservable: Observable<T>, initialValue: T) {
19
+ if (!cache.has(inputObservable)) {
20
+ const entry: Partial<CacheRecord<T>> = {currentValue: initialValue}
21
+ entry.observable = inputObservable.pipe(
22
+ shareReplay({refCount: true, bufferSize: 1}),
23
+ tap(value => (entry.currentValue = value)),
24
+ )
25
+ entry.subscription = entry.observable.subscribe()
26
+
27
+ cache.set(inputObservable, entry as CacheRecord<T>)
28
+ }
29
+ return cache.get(inputObservable)
29
30
  }
30
31
 
31
32
  export function useObservable<T>(observable: Observable<T>): T | undefined
32
33
  export function useObservable<T>(observable: Observable<T>, initialValue: T): T
33
34
  export function useObservable<T>(observable: Observable<T>, initialValue: () => T): T
34
35
  export function useObservable<T>(observable: Observable<T>, initialValue?: T | (() => T)) {
35
- return useObservableSubscription(
36
- useAsObservable(
37
- observable,
38
- pipe(
39
- distinctUntilChanged(),
40
- switchMap(observable =>
41
- merge(
42
- defer(() => of(getValue(initialValue))),
43
- observable,
44
- ),
45
- ),
46
- ),
47
- ),
48
- )
36
+ const [getSnapshot, subscribe] = useMemo(() => {
37
+ const record = getOrCreateStore(observable, getValue(initialValue))!
38
+ return [
39
+ function getSnapshot() {
40
+ return record.currentValue
41
+ },
42
+ function subscribe(callback: (value: T) => void) {
43
+ const sub = record.observable.subscribe(next => callback(next))
44
+ return () => {
45
+ sub.unsubscribe()
46
+ }
47
+ },
48
+ ]
49
+ }, [observable])
50
+
51
+ useIsomorphicEffect(() => {
52
+ return () => {
53
+ getOrCreateStore(observable, getValue(initialValue))!.subscription.unsubscribe()
54
+ }
55
+ }, [observable])
56
+
57
+ return useSyncExternalStore(subscribe, getSnapshot)
49
58
  }
50
59
 
51
60
  export function useMemoObservable<T>(
package/tsconfig.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "include": ["src/**/*"],
3
3
  "compilerOptions": {
4
+ "target": "ES2015",
4
5
  "strict": true,
5
6
  "outDir": "dist",
6
7
  "jsx": "react",