mirror of
https://github.com/re-rxjs/react-rxjs.git
synced 2025-12-08 18:01:51 +00:00
Improve createInput types and behavior
This commit is contained in:
parent
a9c26d305e
commit
beee11d93a
@ -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
|
||||
|
||||
@ -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()
|
||||
})
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user