-
Notifications
You must be signed in to change notification settings - Fork 0
/
butterfly.ts
43 lines (41 loc) · 1.46 KB
/
butterfly.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { BehaviorSubject, Observable } from 'rxjs'
export type StateSetter<T> = T | ((currentValue: T) => T)
/**
* Create an atomic Observable for representation of a small piece of state while splitting
* the read API and write APIs in a way making it easier to protect write APIs from escaping
* API boundaries.
*
* In this case "butterfly" is something of an analogy to "butterfly your shrimp", but also
* just an evocative name that calls back to the origins of this engine's own name.
*
* This is just a simple BehaviorSubject constructor/wrapper that is made to resemble React's
* useState with the idea of making it less likely to leak the subject as a whole across API
* boundaries by thinking of it as a tuple of two to four things, three of which should be
* "private".
* @param startingValue Starting value for atomic
* @returns [observable, next, error, complete]
*/
export function butterfly<T>(
startingValue: T,
): [
observable: Observable<T>,
next: (value: StateSetter<T>) => void,
error: (error: unknown) => void,
complete: () => void,
] {
const subject = new BehaviorSubject(startingValue)
function setState(value: StateSetter<T>) {
if (typeof value === 'function') {
const setter = value as (currentValue: T) => T
subject.next(setter(subject.getValue()))
} else {
subject.next(value)
}
}
return [
subject.asObservable(),
setState,
subject.error.bind(subject),
subject.complete.bind(subject),
]
}