mirror of
https://github.com/re-rxjs/react-rxjs.git
synced 2025-12-08 18:01:51 +00:00
ditch connectInstanceObservable
This commit is contained in:
parent
2e06be1744
commit
87f41c848d
@ -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
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
export { connectObservable } from "./connectObservable"
|
||||
export { connectFactoryObservable } from "./connectFactoryObservable"
|
||||
export { connectInstanceObservable } from "./connectInstanceObservable"
|
||||
export {
|
||||
default as distinctShareReplay,
|
||||
BehaviorObservable,
|
||||
|
||||
@ -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)
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user