Improve createInput types and behavior

This commit is contained in:
Josep M Sobrepere 2020-06-22 23:51:32 +02:00
parent a9c26d305e
commit beee11d93a
2 changed files with 38 additions and 31 deletions

View File

@ -1,9 +1,17 @@
import { Subject, Observable, ReplaySubject } from "rxjs"
import { finalize } from "rxjs/operators"
import { distinctShareReplay } from "./operators/distinct-share-replay"
interface CreateInput {
(): [(key: string) => Observable<void>, (key: string) => void]
<T>(defaultValue?: T): [
(key: string) => Observable<T>,
(key: string, update: T | ((prev: T) => T)) => void,
]
}
const empty = Symbol("empty") as any
export function createInput<T>(defaultValue: T = empty) {
const F = () => false
const createInput_ = <T>(defaultValue: T = empty) => {
const cache = new Map<string, [Subject<T>, { latest: T }, Observable<T>]>()
const getEntry = (key: string) => {
let result = cache.get(key)
@ -13,10 +21,9 @@ export function createInput<T>(defaultValue: T = empty) {
if (defaultValue !== empty) {
subject.next((current.latest = defaultValue))
}
const source = subject.pipe(
finalize(() => cache.delete(key)),
distinctShareReplay(),
)
const source = distinctShareReplay(F, () => cache.delete(key))(
subject,
) as Observable<T>
result = [subject, current, source]
cache.set(key, result)
return result
@ -31,3 +38,5 @@ export function createInput<T>(defaultValue: T = empty) {
const getSource = (key: string) => getEntry(key)[2]
return [getSource, onChange] as const
}
export const createInput = createInput_ as CreateInput

View File

@ -1,4 +1,5 @@
import { createInput } from "../src"
import { scan } from "rxjs/operators"
describe("createInput", () => {
test("it returns the default value", () => {
@ -21,6 +22,28 @@ describe("createInput", () => {
sub.unsubscribe()
})
test("it accepts void inputs", () => {
const [getClicks$, onClick] = createInput()
let latestValue: number | undefined = undefined
const sub = getClicks$("foo")
.pipe(scan(prev => prev + 1, 0))
.subscribe(x => {
latestValue = x
})
expect(latestValue).toBe(undefined)
onClick("foo")
onClick("foo")
onClick("foo")
onClick("foo")
expect(latestValue).toBe(4)
sub.unsubscribe()
})
test("it replays the latest value to new subscriptions", () => {
const [getCounter, setCounter] = createInput(10)
const sub1 = getCounter("foo").subscribe()
@ -53,27 +76,6 @@ describe("createInput", () => {
sub3.unsubscribe()
})
test("it does not trigger updates if the value has not changed", () => {
const [getCounter, setCounter] = createInput(10)
let nUpdates = 0
const sub1 = getCounter("foo").subscribe(() => {
nUpdates += 1
})
expect(nUpdates).toBe(1)
setCounter("foo", 100)
expect(nUpdates).toBe(2)
setCounter("foo", 100)
expect(nUpdates).toBe(2)
setCounter("foo", 100)
expect(nUpdates).toBe(2)
sub1.unsubscribe()
})
test("the setter can also be a function", () => {
const [getCounter, setCounter] = createInput(10)
@ -94,10 +96,6 @@ describe("createInput", () => {
expect(nUpdates).toBe(3)
expect(latestValue).toBe(40)
setCounter("foo", x => x)
expect(nUpdates).toBe(3)
expect(latestValue).toBe(40)
sub1.unsubscribe()
})
})