From ccf81ed6030495638c3fb0a89d313eda48f136d3 Mon Sep 17 00:00:00 2001 From: Josep M Sobrepere Date: Wed, 14 Oct 2020 17:46:52 +0200 Subject: [PATCH] perf(core): react-enhancer only returns a function --- .../core/src/bind/connectFactoryObservable.ts | 19 ++--- packages/core/src/bind/connectObservable.ts | 4 +- .../core/src/internal/BehaviorObservable.ts | 3 +- packages/core/src/internal/react-enhancer.ts | 75 +++++++++---------- packages/core/src/internal/share-latest.ts | 8 +- packages/core/src/internal/useObservable.ts | 11 +-- 6 files changed, 56 insertions(+), 64 deletions(-) diff --git a/packages/core/src/bind/connectFactoryObservable.ts b/packages/core/src/bind/connectFactoryObservable.ts index e6e5bad..2232861 100644 --- a/packages/core/src/bind/connectFactoryObservable.ts +++ b/packages/core/src/bind/connectFactoryObservable.ts @@ -30,14 +30,11 @@ export default function connectFactoryObservable( (...args: A) => Exclude, (...args: A) => Observable, ] { - const cache = new NestedMap< - A, - [BehaviorObservable, BehaviorObservable] - >() + const cache = new NestedMap, () => O]>() const getSharedObservables$ = ( input: A, - ): [BehaviorObservable, BehaviorObservable] => { + ): [BehaviorObservable, () => O] => { for (let i = input.length - 1; input[i] === undefined && i > -1; i--) { input.splice(-1) } @@ -68,11 +65,12 @@ export default function connectFactoryObservable( return source$.subscribe(subscriber) }) as BehaviorObservable - const reactObservable$ = reactEnhancer(publicShared$) + publicShared$.getValue = sharedObservable$.getValue + const reactGetValue = reactEnhancer(publicShared$) - const result: [BehaviorObservable, BehaviorObservable] = [ + const result: [BehaviorObservable, () => O] = [ publicShared$, - reactObservable$, + reactGetValue, ] cache.set(keys, result) @@ -80,7 +78,10 @@ export default function connectFactoryObservable( } return [ - (...input: A) => useObservable(getSharedObservables$(input)[1]), + (...input: A) => { + const [source$, getValue] = getSharedObservables$(input) + return useObservable(source$, getValue) + }, (...input: A) => getSharedObservables$(input)[0], ] } diff --git a/packages/core/src/bind/connectObservable.ts b/packages/core/src/bind/connectObservable.ts index a2da331..7a20156 100644 --- a/packages/core/src/bind/connectObservable.ts +++ b/packages/core/src/bind/connectObservable.ts @@ -20,7 +20,7 @@ import { useObservable } from "../internal/useObservable" */ export default function connectObservable(observable: Observable) { const sharedObservable$ = shareLatest(observable, false) - const reactObservable$ = reactEnhancer(sharedObservable$) - const useStaticObservable = () => useObservable(reactObservable$) + const getValue = reactEnhancer(sharedObservable$) + const useStaticObservable = () => useObservable(sharedObservable$, getValue) return [useStaticObservable, sharedObservable$] as const } diff --git a/packages/core/src/internal/BehaviorObservable.ts b/packages/core/src/internal/BehaviorObservable.ts index f62a1c8..7095c18 100644 --- a/packages/core/src/internal/BehaviorObservable.ts +++ b/packages/core/src/internal/BehaviorObservable.ts @@ -1,5 +1,6 @@ import { Observable } from "rxjs" +import { SUSPENSE } from "../SUSPENSE" export interface BehaviorObservable extends Observable { - getValue: () => any + getValue: () => T | typeof SUSPENSE } diff --git a/packages/core/src/internal/react-enhancer.ts b/packages/core/src/internal/react-enhancer.ts index 658e236..8957b7f 100644 --- a/packages/core/src/internal/react-enhancer.ts +++ b/packages/core/src/internal/react-enhancer.ts @@ -1,16 +1,17 @@ -import { Observable } from "rxjs" import { SUSPENSE } from "../SUSPENSE" import { BehaviorObservable } from "./BehaviorObservable" import { EMPTY_VALUE } from "./empty-value" -const reactEnhancer = (source$: Observable): BehaviorObservable => { - const result = new Observable((subscriber) => - source$.subscribe(subscriber), - ) as BehaviorObservable - +const reactEnhancer = (source$: BehaviorObservable): (() => T) => { let promise: Promise | undefined let error: any = EMPTY_VALUE - const getValue = (): T => { + + return (): T => { + const currentValue = source$.getValue() + if (currentValue !== SUSPENSE && currentValue !== EMPTY_VALUE) { + return currentValue + } + let timeoutToken if (error !== EMPTY_VALUE) { clearTimeout(timeoutToken) @@ -20,44 +21,38 @@ const reactEnhancer = (source$: Observable): BehaviorObservable => { throw error } - try { - return (source$ as BehaviorObservable).getValue() - } catch (e) { - if (promise) throw promise + if (promise) throw promise - let value: typeof EMPTY_VALUE | T = EMPTY_VALUE + let value: typeof EMPTY_VALUE | T = EMPTY_VALUE - promise = new Promise((res) => { - const subscription = result.subscribe( - (v) => { - if (v !== (SUSPENSE as any)) { - value = v - subscription && subscription.unsubscribe() - res(v) - } - }, - (e) => { - error = e - timeoutToken = setTimeout(() => { - error = EMPTY_VALUE - }, 50) - res() - }, - ) - if (value !== EMPTY_VALUE) { - subscription.unsubscribe() - } - }).finally(() => { - promise = undefined - }) + promise = new Promise((res) => { + const subscription = source$.subscribe( + (v) => { + if (v !== (SUSPENSE as any)) { + value = v + subscription && subscription.unsubscribe() + res(v) + } + }, + (e) => { + error = e + timeoutToken = setTimeout(() => { + error = EMPTY_VALUE + }, 50) + res() + }, + ) + if (value !== EMPTY_VALUE) { + subscription.unsubscribe() + } + }).finally(() => { + promise = undefined + }) - if (value !== EMPTY_VALUE) return value + if (value !== EMPTY_VALUE) return value - throw error !== EMPTY_VALUE ? error : promise - } + throw error !== EMPTY_VALUE ? error : promise } - result.getValue = getValue - return result } export default reactEnhancer diff --git a/packages/core/src/internal/share-latest.ts b/packages/core/src/internal/share-latest.ts index bf5e9db..c75baf8 100644 --- a/packages/core/src/internal/share-latest.ts +++ b/packages/core/src/internal/share-latest.ts @@ -1,5 +1,4 @@ import { Observable, Subscription, Subject, noop } from "rxjs" -import { SUSPENSE } from "../SUSPENSE" import { BehaviorObservable } from "./BehaviorObservable" import { EMPTY_VALUE } from "./empty-value" @@ -58,12 +57,7 @@ const shareLatest = ( } }) as BehaviorObservable - result.getValue = () => { - if (currentValue === EMPTY_VALUE || currentValue === (SUSPENSE as any)) { - throw currentValue - } - return currentValue - } + result.getValue = () => currentValue return result } diff --git a/packages/core/src/internal/useObservable.ts b/packages/core/src/internal/useObservable.ts index b9dcaae..730dd1d 100644 --- a/packages/core/src/internal/useObservable.ts +++ b/packages/core/src/internal/useObservable.ts @@ -1,12 +1,13 @@ import { useEffect, useState } from "react" -import { BehaviorObservable } from "./BehaviorObservable" import { SUSPENSE } from "../SUSPENSE" import { EMPTY_VALUE } from "./empty-value" +import { Observable } from "rxjs" export const useObservable = ( - source$: BehaviorObservable, + source$: Observable, + getValue: () => O, ): Exclude => { - const [state, setState] = useState(source$.getValue) + const [state, setState] = useState(getValue) useEffect(() => { let prevVal: O | typeof SUSPENSE = EMPTY_VALUE @@ -14,7 +15,7 @@ export const useObservable = ( const onNext = (value: O | typeof SUSPENSE) => { if (value === SUSPENSE) { - setState(source$.getValue) + setState(getValue) } else if (!Object.is(value, prevVal)) { setState(value) } @@ -37,5 +38,5 @@ export const useObservable = ( return () => subscription.unsubscribe() }, [source$]) - return state + return state as Exclude }