ditch connectInstanceObservable

This commit is contained in:
Josep M Sobrepere 2020-06-09 20:54:59 +02:00
parent 2e06be1744
commit 87f41c848d
3 changed files with 0 additions and 227 deletions

View File

@ -1,112 +0,0 @@
import { Observable, BehaviorSubject } from "rxjs"
import { FactoryObservableOptions } from "./options"
import { map } from "rxjs/operators"
import { useEffect, useRef, useState } from "react"
import delayUnsubscription from "./operators/delay-unsubscription"
import { distinctShareReplay, BehaviorObservable } from "./"
interface ConnectInstanceObservable {
<I, T1, T2, O>(
getObservable: (props$: Observable<[T1, T2]>) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1, b: T2) => O | I
<I, T1, T2, T3, O>(
getObservable: (props$: Observable<[T1, T2, T3]>) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1, b: T2, c: T3) => O | I
<I, T1, T2, T3, T4, O>(
getObservable: (props$: Observable<[T1, T2, T3, T4]>) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1, b: T2, c: T3, d: T4) => O | I
<I, T1, T2, T3, T4, T5, O>(
getObservable: (props$: Observable<[T1, T2, T3, T4, T5]>) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1, b: T2, c: T3, d: T4, e: T5) => O | I
<I, T1, T2, T3, T4, T5, T6, O>(
getObservable: (
props$: Observable<[T1, T2, T3, T4, T5, T6]>,
) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1, b: T2, c: T3, d: T4, e: T5, f: T6) => O | I
<I, T1, T2, T3, T4, T5, T6, T7, O>(
getObservable: (
props$: Observable<[T1, T2, T3, T4, T5, T6, T7]>,
) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7) => O | I
<I, T1, T2, T3, T4, T5, T6, T7, T8, O>(
getObservable: (
props$: Observable<[T1, T2, T3, T4, T5, T6, T7, T8]>,
) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8) => O | I
<I, T1, T2, T3, T4, T5, T6, T7, T8, T9, O>(
getObservable: (
props$: Observable<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>,
) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1, b: T2, c: T3, d: T4, e: T5, f: T6, g: T7, h: T8, i: T9) => O | I
<I, T1, O>(
getObservable: (props$: Observable<T1>) => Observable<O>,
initialValue: I,
options?: FactoryObservableOptions<O>,
): (a: T1) => O | I
}
const flatSingleTuple = (src: Observable<Array<any>>) =>
map((inputs: any) => (inputs.length === 1 ? inputs[0] : inputs))(src)
const cache = new WeakMap<
React.MutableRefObject<any>,
BehaviorObservable<any>
>()
const defaultValue: any = {}
export const connectInstanceObservable: ConnectInstanceObservable = (
getObservable: any,
unsubscribeGraceTime = 200,
) => {
const getSuspendedState = (ref: React.MutableRefObject<any>, input: any) => {
let source$ = cache.get(ref)
if (source$) {
return source$.getValue()
}
const subject = new BehaviorSubject(input)
ref.current = subject
source$ = delayUnsubscription(unsubscribeGraceTime)(
distinctShareReplay()(subject.pipe(flatSingleTuple, getObservable)),
)
cache.set(ref, source$)
return source$.getValue()
}
const useInstance = (...input: any) => {
const subjectRef = useRef<any>()
const [state, setState] = useState(defaultValue)
useEffect(() => {
const subscription = cache.get(subjectRef)!.subscribe(setState)
return () => subscription.unsubscribe()
}, [])
useEffect(() => {
if (subjectRef.current!.i) {
subjectRef.current!.next(input)
}
subjectRef.current!.i = 1
}, input)
return state !== defaultValue ? state : getSuspendedState(subjectRef, input)
}
return useInstance as any
}

View File

@ -1,6 +1,5 @@
export { connectObservable } from "./connectObservable"
export { connectFactoryObservable } from "./connectFactoryObservable"
export { connectInstanceObservable } from "./connectInstanceObservable"
export {
default as distinctShareReplay,
BehaviorObservable,

View File

@ -1,114 +0,0 @@
import { connectInstanceObservable } from "../src"
import { of, Subject, Observable, combineLatest, merge } from "rxjs"
import { renderHook, act } from "@testing-library/react-hooks"
import {
ignoreElements,
switchMap,
distinctUntilChanged,
scan,
shareReplay,
tap,
map,
startWith,
} from "rxjs/operators"
import { groupInMap } from "@josepot/rxjs-utils"
const getPriceData = (key: string, period: number) =>
of(
key
.split("")
.map((_, idx) => idx)
.concat(period),
)
const tagAndPeriod$ = new Subject<[string, number]>()
const tagsData$ = tagAndPeriod$.pipe(
groupInMap(
([tag]) => tag,
stream$ =>
stream$.pipe(
scan((maxPeriod, [, period]) => Math.max(maxPeriod, period), 0),
distinctUntilChanged(),
switchMap(period => getPriceData(stream$.key, period)),
),
),
startWith(new Map()),
shareReplay(1),
)
const empty: number[] = []
const usePriceData = connectInstanceObservable(
(props$: Observable<[string, number]>) => {
const plug$ = props$.pipe(tap(tagAndPeriod$), ignoreElements())
const data$ = props$.pipe(
map(([tag]) => tag),
distinctUntilChanged(),
switchMap(tag =>
tagsData$.pipe(
map(tags => tags.get(tag) || empty),
distinctUntilChanged(),
),
),
)
const periods$ = props$.pipe(
map(([, period]) => period),
distinctUntilChanged(),
)
return merge(combineLatest(data$, periods$), plug$).pipe(
map(([data, period]) => data.slice(-period)),
) as Observable<number[]>
},
[],
)
describe("connectInstanceObservable", () => {
it("works", () => {
const { result, rerender } = renderHook(
({ tag, period }: { tag: string; period: number }) =>
usePriceData(tag, period),
{ initialProps: { tag: "hello", period: 9 } },
)
expect(result.current).toEqual([0, 1, 2, 3, 4, 9])
act(() => {
rerender({ tag: "hello", period: 2 })
})
expect(result.current).toEqual([4, 9])
act(() => {
rerender({ tag: "ups", period: 8 })
})
expect(result.current).toEqual([0, 1, 2, 8])
act(() => {
rerender({ tag: "012345", period: 10 })
})
expect(result.current).toEqual([0, 1, 2, 3, 4, 5, 10])
})
it("also works with one argument", () => {
const useDoubles = connectInstanceObservable(
(props$: Observable<number>) => props$.pipe(map(x => x * 2)),
null,
)
const { result, rerender } = renderHook(
({ input }: { input: number }) => useDoubles(input),
{ initialProps: { input: 1 } },
)
expect(result.current).toEqual(2)
act(() => {
rerender({ input: 2 })
})
expect(result.current).toEqual(4)
})
})