mirror of
https://github.com/re-rxjs/react-rxjs.git
synced 2025-12-08 18:01:51 +00:00
feat(bind): nested keys accept optional args
This commit is contained in:
parent
8eac0e13ce
commit
2d58fbab32
@ -127,6 +127,13 @@ describe("connectFactoryObservable", () => {
|
||||
expect(result.current).toBe(2)
|
||||
})
|
||||
|
||||
it("handles optional args correctly", () => {
|
||||
const [, getNumber$] = bind((x: number, y?: number) => of(x + (y ?? 0)))
|
||||
|
||||
expect(getNumber$(5)).toBe(getNumber$(5, undefined))
|
||||
expect(getNumber$(6, undefined)).toBe(getNumber$(6))
|
||||
})
|
||||
|
||||
it("suspends the component when the factory-observable hasn't emitted yet.", async () => {
|
||||
const [useDelayedNumber] = bind((x: number) => of(x).pipe(delay(50)))
|
||||
const Result: React.FC<{ input: number }> = (p) => (
|
||||
|
||||
@ -6,6 +6,74 @@ import { useObservable } from "../internal/useObservable"
|
||||
import { SUSPENSE } from "../SUSPENSE"
|
||||
import { takeUntilComplete } from "../internal/take-until-complete"
|
||||
|
||||
/**
|
||||
* Accepts: A factory function that returns an Observable.
|
||||
*
|
||||
* Returns [1, 2]
|
||||
* 1. A React Hook function with the same parameters as the factory function.
|
||||
* This hook will yield the latest update from the observable returned from
|
||||
* the factory function.
|
||||
* 2. A `sharedLatest` version of the observable generated by the factory
|
||||
* function that can be used for composing other streams that depend on it.
|
||||
* The shared subscription is closed as soon as there are no subscribers to
|
||||
* that observable.
|
||||
*
|
||||
* @param getObservable Factory of observables. The arguments of this function
|
||||
* will be the ones used in the hook.
|
||||
* @param unsubscribeGraceTime (= 200): Amount of time in ms that the shared
|
||||
* observable should wait before unsubscribing from the source observable when
|
||||
* there are no new subscribers.
|
||||
*
|
||||
* @remarks If the Observable doesn't synchronously emit a value upon the first
|
||||
* subscription, then the hook will leverage React Suspense while it's waiting
|
||||
* for the first value.
|
||||
*/
|
||||
export default function connectFactoryObservable<A extends [], O>(
|
||||
getObservable: (...args: A) => Observable<O>,
|
||||
unsubscribeGraceTime: number,
|
||||
): [
|
||||
(...args: A) => Exclude<O, typeof SUSPENSE>,
|
||||
(...args: A) => Observable<O>,
|
||||
] {
|
||||
const cache = new NestedMap<A, [Observable<O>, BehaviorObservable<O>]>()
|
||||
|
||||
const getSharedObservables$ = (
|
||||
input: A,
|
||||
): [Observable<O>, BehaviorObservable<O>] => {
|
||||
for (let i = input.length - 1; input[i] === undefined && i > -1; i--) {
|
||||
input.splice(-1)
|
||||
}
|
||||
const keys = ([input.length, ...input] as any) as A
|
||||
const cachedVal = cache.get(keys)
|
||||
|
||||
if (cachedVal !== undefined) {
|
||||
return cachedVal
|
||||
}
|
||||
|
||||
const sharedObservable$ = shareLatest(getObservable(...input), () => {
|
||||
cache.delete(keys)
|
||||
})
|
||||
|
||||
const reactObservable$ = reactEnhancer(
|
||||
sharedObservable$,
|
||||
unsubscribeGraceTime,
|
||||
)
|
||||
|
||||
const result: [Observable<O>, BehaviorObservable<O>] = [
|
||||
takeUntilComplete(sharedObservable$),
|
||||
reactObservable$,
|
||||
]
|
||||
|
||||
cache.set(keys, result)
|
||||
return result
|
||||
}
|
||||
|
||||
return [
|
||||
(...input: A) => useObservable(getSharedObservables$(input)[1]),
|
||||
(...input: A) => getSharedObservables$(input)[0],
|
||||
]
|
||||
}
|
||||
|
||||
class NestedMap<K extends [], V extends Object> {
|
||||
private root: Map<K, any>
|
||||
constructor() {
|
||||
@ -51,69 +119,3 @@ class NestedMap<K extends [], V extends Object> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const emptyInput = [0]
|
||||
/**
|
||||
* Accepts: A factory function that returns an Observable.
|
||||
*
|
||||
* Returns [1, 2]
|
||||
* 1. A React Hook function with the same parameters as the factory function.
|
||||
* This hook will yield the latest update from the observable returned from
|
||||
* the factory function.
|
||||
* 2. A `sharedLatest` version of the observable generated by the factory
|
||||
* function that can be used for composing other streams that depend on it.
|
||||
* The shared subscription is closed as soon as there are no subscribers to
|
||||
* that observable.
|
||||
*
|
||||
* @param getObservable Factory of observables. The arguments of this function
|
||||
* will be the ones used in the hook.
|
||||
* @param unsubscribeGraceTime (= 200): Amount of time in ms that the shared
|
||||
* observable should wait before unsubscribing from the source observable when
|
||||
* there are no new subscribers.
|
||||
*
|
||||
* @remarks If the Observable doesn't synchronously emit a value upon the first
|
||||
* subscription, then the hook will leverage React Suspense while it's waiting
|
||||
* for the first value.
|
||||
*/
|
||||
export default function connectFactoryObservable<A extends [], O>(
|
||||
getObservable: (...args: A) => Observable<O>,
|
||||
unsubscribeGraceTime: number,
|
||||
): [
|
||||
(...args: A) => Exclude<O, typeof SUSPENSE>,
|
||||
(...args: A) => Observable<O>,
|
||||
] {
|
||||
const cache = new NestedMap<A, [Observable<O>, BehaviorObservable<O>]>()
|
||||
|
||||
const getSharedObservables$ = (
|
||||
input: A,
|
||||
): [Observable<O>, BehaviorObservable<O>] => {
|
||||
const keys = input.length > 0 ? input : (emptyInput as A)
|
||||
const cachedVal = cache.get(keys)
|
||||
|
||||
if (cachedVal !== undefined) {
|
||||
return cachedVal
|
||||
}
|
||||
|
||||
const sharedObservable$ = shareLatest(getObservable(...input), () => {
|
||||
cache.delete(keys)
|
||||
})
|
||||
|
||||
const reactObservable$ = reactEnhancer(
|
||||
sharedObservable$,
|
||||
unsubscribeGraceTime,
|
||||
)
|
||||
|
||||
const result: [Observable<O>, BehaviorObservable<O>] = [
|
||||
takeUntilComplete(sharedObservable$),
|
||||
reactObservable$,
|
||||
]
|
||||
|
||||
cache.set(keys, result)
|
||||
return result
|
||||
}
|
||||
|
||||
return [
|
||||
(...input: A) => useObservable(getSharedObservables$(input)[1]),
|
||||
(...input: A) => getSharedObservables$(input)[0],
|
||||
]
|
||||
}
|
||||
|
||||
@ -46,18 +46,12 @@ export function bind<T>(
|
||||
* subscription, then the hook will leverage React Suspense while it's waiting
|
||||
* for the first value.
|
||||
*/
|
||||
export function bind<
|
||||
A extends (number | string | boolean | null | Object | Symbol)[],
|
||||
O
|
||||
>(
|
||||
export function bind<A extends unknown[], O>(
|
||||
getObservable: (...args: A) => Observable<O>,
|
||||
unsubscribeGraceTime?: number,
|
||||
): [(...args: A) => Exclude<O, typeof SUSPENSE>, (...args: A) => Observable<O>]
|
||||
|
||||
export function bind<
|
||||
A extends (number | string | boolean | null | Object | Symbol)[],
|
||||
O
|
||||
>(
|
||||
export function bind<A extends unknown[], O>(
|
||||
obs: ((...args: A) => Observable<O>) | Observable<O>,
|
||||
unsubscribeGraceTime = 200,
|
||||
) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user