Created
July 21, 2021 20:03
-
-
Save twfarland/03fe3afb8a3dc07a1802cfac89a666f8 to your computer and use it in GitHub Desktop.
Experiment in doing FRP directly with svelte stores (no external library like RxJS)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
derived, | |
writable, | |
Readable, | |
Unsubscriber, | |
readable, | |
} from "svelte/store"; | |
export function map<A, B>( | |
mapper: (value: A) => B, | |
store: Readable<A> | |
): Readable<B> { | |
return derived(store, mapper); | |
} | |
export function filter<A>( | |
predicate: (value: A) => boolean, | |
store: Readable<A> | |
): Readable<A> { | |
return derived(store, (value, set) => { | |
if (predicate(value)) { | |
set(value); | |
} | |
}); | |
} | |
export function reduce<A, B>( | |
reducer: (value: A, accumulated: B) => B, | |
seed: B, | |
store: Readable<A> | |
): Readable<B> { | |
let acc: B = seed; | |
return derived(store, (value, set) => { | |
set(acc); | |
acc = reducer(value, acc); | |
}); | |
} | |
export function dropRepeats<A>(store: Readable<A>): Readable<A> { | |
let prev: A | undefined = undefined; | |
return derived(store, (value, set) => { | |
if (value !== prev) { | |
prev = value; | |
set(value); | |
} | |
}); | |
} | |
export function flatMap<A, B>( | |
lift: (value: A) => Readable<B>, | |
store: Readable<A>, | |
initialValue: B | |
): Readable<B> { | |
const flat = writable(initialValue); | |
store.subscribe((value) => { | |
lift(value).subscribe(flat.set); | |
}); | |
return flat; | |
} | |
export function flatMapLatest<A, B>( | |
lift: (value: A) => Readable<B>, | |
store: Readable<A>, | |
initialValue: B | |
): Readable<B> { | |
const flat = writable(initialValue); | |
let unsubscriber: Unsubscriber | undefined = undefined; | |
store.subscribe((value) => { | |
if (unsubscriber) { | |
unsubscriber(); | |
} | |
unsubscriber = lift(value).subscribe(flat.set); | |
}); | |
return flat; | |
} | |
export function merge<Union>( | |
stores: Array<Readable<Union>>, | |
initialValue: Union | |
) { | |
const merged = writable<Union>(initialValue); | |
for (const store of stores) { | |
store.subscribe((v) => merged.set(v)); | |
} | |
return merged; | |
} | |
export function sampleOn<A, B>( | |
store1: Readable<A>, | |
store2: Readable<B>, | |
initialValue: A | |
): Readable<A> { | |
let value1 = initialValue; | |
store1.subscribe((value) => { | |
value1 = value; | |
}); | |
return derived(store2, (_, set) => { | |
set(value1); | |
}); | |
} | |
export function slidingWindow<A>( | |
length: number, | |
store: Readable<A> | |
): Readable<A[]> { | |
let frame: A[] = []; | |
return derived(store, (value) => { | |
if (frame.length > length - 1) frame.shift(); | |
frame.push(value); | |
return [...frame]; | |
}); | |
} | |
export function debounce<A>( | |
quiet: number, | |
store: Readable<A>, | |
initialValue: A | |
): Readable<A> { | |
return flatMapLatest( | |
(value) => { | |
return fromCallback((callback) => { | |
setTimeout(() => { | |
callback(value); | |
}, quiet); | |
}); | |
}, | |
store, | |
initialValue | |
); | |
} | |
export function fromCallback<A>( | |
callback: (f: (value: A) => void) => void | |
): Readable<A> { | |
return readable<A>(undefined, (set) => { | |
callback((value) => { | |
set(value); | |
}); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment