Skip to content

Commit e86dfd3

Browse files
committed
feat(bind): factory with nested keys
1 parent a801f81 commit e86dfd3

File tree

2 files changed

+65
-14
lines changed

2 files changed

+65
-14
lines changed

packages/core/src/bind/connectFactoryObservable.ts

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,53 @@ import { useObservable } from "../internal/useObservable"
66
import { SUSPENSE } from "../SUSPENSE"
77
import { takeUntilComplete } from "../internal/take-until-complete"
88

9+
class NestedMap<K extends [], V extends Object> {
10+
private root: Map<K, any>
11+
constructor() {
12+
this.root = new Map()
13+
}
14+
15+
get(keys: K[]): V | undefined {
16+
let current: any = this.root
17+
for (let i = 0; i < keys.length; i++) {
18+
current = current.get(keys[i])
19+
if (!current) return undefined
20+
}
21+
return current
22+
}
23+
24+
set(keys: K[], value: V): void {
25+
let current: Map<K, any> = this.root
26+
let i
27+
for (i = 0; i < keys.length - 1; i++) {
28+
const key = keys[i]
29+
let nextCurrent = current.get(key)
30+
if (!nextCurrent) {
31+
nextCurrent = new Map<K, any>()
32+
current.set(key, nextCurrent)
33+
}
34+
current = nextCurrent
35+
}
36+
current.set(keys[i], value)
37+
}
38+
39+
delete(keys: K[]): void {
40+
const maps: Map<K, any>[] = [this.root]
41+
let current: Map<K, any> = this.root
42+
43+
for (let i = 0; i < keys.length - 1; i++) {
44+
maps.push((current = current.get(keys[i])))
45+
}
46+
47+
let mapIdx = maps.length - 1
48+
maps[mapIdx].delete(keys[mapIdx])
49+
50+
while (--mapIdx > -1 && maps[mapIdx].get(keys[mapIdx]).size === 0) {
51+
maps[mapIdx].delete(keys[mapIdx])
52+
}
53+
}
54+
}
55+
956
/**
1057
* Accepts: A factory function that returns an Observable.
1158
*
@@ -28,30 +75,28 @@ import { takeUntilComplete } from "../internal/take-until-complete"
2875
* subscription, then the hook will leverage React Suspense while it's waiting
2976
* for the first value.
3077
*/
31-
export default function connectFactoryObservable<
32-
A extends (number | string | boolean | null)[],
33-
O
34-
>(
78+
const emptyInput = [0]
79+
export default function connectFactoryObservable<A extends [], O>(
3580
getObservable: (...args: A) => Observable<O>,
3681
unsubscribeGraceTime: number,
3782
): [
3883
(...args: A) => Exclude<O, typeof SUSPENSE>,
3984
(...args: A) => Observable<O>,
4085
] {
41-
const cache = new Map<string, [Observable<O>, BehaviorObservable<O>]>()
86+
const cache = new NestedMap<A, [Observable<O>, BehaviorObservable<O>]>()
4287

4388
const getSharedObservables$ = (
44-
...input: A
89+
input: A,
4590
): [Observable<O>, BehaviorObservable<O>] => {
46-
const key = JSON.stringify(input)
47-
const cachedVal = cache.get(key)
91+
const keys = input.length > 0 ? input : (emptyInput as A)
92+
const cachedVal = cache.get(keys)
4893

4994
if (cachedVal !== undefined) {
5095
return cachedVal
5196
}
5297

5398
const sharedObservable$ = shareLatest(getObservable(...input), () => {
54-
cache.delete(key)
99+
cache.delete(keys)
55100
})
56101

57102
const reactObservable$ = reactEnhancer(
@@ -64,12 +109,12 @@ export default function connectFactoryObservable<
64109
reactObservable$,
65110
]
66111

67-
cache.set(key, result)
112+
cache.set(keys, result)
68113
return result
69114
}
70115

71116
return [
72-
(...input: A) => useObservable(getSharedObservables$(...input)[1]),
73-
(...input: A) => getSharedObservables$(...input)[0],
117+
(...input: A) => useObservable(getSharedObservables$(input)[1]),
118+
(...input: A) => getSharedObservables$(input)[0],
74119
]
75120
}

packages/core/src/bind/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,18 @@ export function bind<T>(
4646
* subscription, then the hook will leverage React Suspense while it's waiting
4747
* for the first value.
4848
*/
49-
export function bind<A extends (number | string | boolean | null)[], O>(
49+
export function bind<
50+
A extends (number | string | boolean | null | Object | Symbol)[],
51+
O
52+
>(
5053
getObservable: (...args: A) => Observable<O>,
5154
unsubscribeGraceTime?: number,
5255
): [(...args: A) => Exclude<O, typeof SUSPENSE>, (...args: A) => Observable<O>]
5356

54-
export function bind<A extends (number | string | boolean | null)[], O>(
57+
export function bind<
58+
A extends (number | string | boolean | null | Object | Symbol)[],
59+
O
60+
>(
5561
obs: ((...args: A) => Observable<O>) | Observable<O>,
5662
unsubscribeGraceTime = 200,
5763
) {

0 commit comments

Comments
 (0)