Skip to content

Commit 4ffa89c

Browse files
committed
feat(bind): nested keys accept optional args
1 parent 794f1e5 commit 4ffa89c

File tree

3 files changed

+59
-56
lines changed

3 files changed

+59
-56
lines changed

packages/core/src/bind/connectFactoryObservable.test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ describe("connectFactoryObservable", () => {
127127
expect(result.current).toBe(2)
128128
})
129129

130+
it("handles optional args correctly", () => {
131+
const [, getNumber$] = bind((x: number, y?: number) => of(x + (y ?? 0)))
132+
133+
expect(getNumber$(5)).toBe(getNumber$(5, undefined))
134+
expect(getNumber$(6, undefined)).toBe(getNumber$(6))
135+
})
136+
130137
it("suspends the component when the factory-observable hasn't emitted yet.", async () => {
131138
const [useDelayedNumber] = bind((x: number) => of(x).pipe(delay(50)))
132139
const Result: React.FC<{ input: number }> = (p) => (

packages/core/src/bind/connectFactoryObservable.ts

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,6 @@ 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-
let nextCurrent = current.get(keys[i])
29-
if (!nextCurrent) {
30-
nextCurrent = new Map<K, any>()
31-
current.set(keys[i], nextCurrent)
32-
}
33-
current = nextCurrent
34-
}
35-
current.set(keys[i], value)
36-
}
37-
38-
delete(keys: K[]): void {
39-
const maps: Map<K, any>[] = [this.root]
40-
let current: Map<K, any> = this.root
41-
42-
for (let i = 0; i < keys.length - 1; i++) {
43-
maps.push((current = current.get(keys[i])))
44-
}
45-
46-
let mapIdx = maps.length - 1
47-
maps[mapIdx].delete(keys[mapIdx])
48-
49-
while (--mapIdx > -1 && maps[mapIdx].get(keys[mapIdx]).size === 0) {
50-
maps[mapIdx].delete(keys[mapIdx])
51-
}
52-
}
53-
}
54-
55-
const emptyInput = [0]
569
/**
5710
* Accepts: A factory function that returns an Observable.
5811
*
@@ -87,7 +40,10 @@ export default function connectFactoryObservable<A extends [], O>(
8740
const getSharedObservables$ = (
8841
input: A,
8942
): [Observable<O>, BehaviorObservable<O>] => {
90-
const keys = input.length > 0 ? input : (emptyInput as A)
43+
for (let i = input.length - 1; input[i] === undefined && i > -1; i--) {
44+
input.splice(-1)
45+
}
46+
const keys = ([input.length, ...input] as any) as A
9147
const cachedVal = cache.get(keys)
9248

9349
if (cachedVal !== undefined) {
@@ -117,3 +73,49 @@ export default function connectFactoryObservable<A extends [], O>(
11773
(...input: A) => getSharedObservables$(input)[0],
11874
]
11975
}
76+
77+
class NestedMap<K extends [], V extends Object> {
78+
private root: Map<K, any>
79+
constructor() {
80+
this.root = new Map()
81+
}
82+
83+
get(keys: K[]): V | undefined {
84+
let current: any = this.root
85+
for (let i = 0; i < keys.length; i++) {
86+
current = current.get(keys[i])
87+
if (!current) return undefined
88+
}
89+
return current
90+
}
91+
92+
set(keys: K[], value: V): void {
93+
let current: Map<K, any> = this.root
94+
let i
95+
for (i = 0; i < keys.length - 1; i++) {
96+
let nextCurrent = current.get(keys[i])
97+
if (!nextCurrent) {
98+
nextCurrent = new Map<K, any>()
99+
current.set(keys[i], nextCurrent)
100+
}
101+
current = nextCurrent
102+
}
103+
current.set(keys[i], value)
104+
}
105+
106+
delete(keys: K[]): void {
107+
const maps: Map<K, any>[] = [this.root]
108+
let current: Map<K, any> = this.root
109+
110+
for (let i = 0; i < keys.length - 1; i++) {
111+
maps.push((current = current.get(keys[i])))
112+
}
113+
114+
let mapIdx = maps.length - 1
115+
maps[mapIdx].delete(keys[mapIdx])
116+
117+
while (--mapIdx > -1 && maps[mapIdx].get(keys[mapIdx]).size === 0) {
118+
maps[mapIdx].delete(keys[mapIdx])
119+
}
120+
}
121+
}

packages/core/src/bind/index.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,12 @@ 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<
50-
A extends (number | string | boolean | null | Object | Symbol)[],
51-
O
52-
>(
49+
export function bind<A extends unknown[], O>(
5350
getObservable: (...args: A) => Observable<O>,
5451
unsubscribeGraceTime?: number,
5552
): [(...args: A) => Exclude<O, typeof SUSPENSE>, (...args: A) => Observable<O>]
5653

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

0 commit comments

Comments
 (0)