Skip to content

Instantly share code, notes, and snippets.

@loilo

loilo/readme.md

Last active Jun 9, 2020
Embed
What would you like to do?
Tiny Reactive Store

Tiny Reactive Store

This is a tiny (≈250b minzipped) implementation of a reactive store. It's heavily inspired by Svelte's store, but it's even leaner than that (no write-protected store, no asynchronous derived data):

import { value, computed } from 'store.mjs'

const bytes = value(4300)
const kbNum = computed(bytes, bytes => bytes / 1024)
const kbStr = computed(kbNum, kbNum => `${kbNum.toFixed(2)} KB`)

console.log(bytes.value, kbNum.value, kbStr.value)
// 4300, 4.19921875, "4.20 KB"

bytes.value = 5120
console.log(bytes.value, kbNum.value, kbStr.value)
// 5120, 5, "5.00 KB"
/**
* Helper function to create a pub/sub system
*/
function watcher() {
const callbacks = new Set()
return {
/**
* Watch the value for changes
*
* @param callback The listener to attach to this watcher
*/
watch(callback) {
callbacks.add(callback)
return () => callbacks.delete(callback)
},
/**
* Share a new value with all watchers
*
* @param value The new value
* @param oldValue The previous value
*/
dispatch(value, oldValue) {
callbacks.forEach(callback => callback(value, oldValue))
}
}
}
/**
* Create an observable value
*
* @param {any} initial The initial value
*/
export function value(initial) {
let current = initial
const { watch, dispatch } = watcher()
return {
watch,
/**
* Get the current value
*/
get value() {
return current
},
/**
* Set the value
*/
set value(newValue) {
if (newValue !== current) {
const oldValue = current
current = newValue
dispatch(current, oldValue)
}
}
}
}
/**
* Create an observable derived value
*
* @param {...any} dependencies The dependency values to track
* @param {function} callback The factory creating a new value from the dependencies
*/
export function computed(...args) {
const dependencies = args
const callback = args.pop()
const compute = () =>
callback(...dependencies.map(dependency => dependency.value))
const current = value(compute())
const { watch, dispatch } = watcher()
current.watch(dispatch)
for (const dependency of dependencies) {
dependency.watch(() => (current.value = compute()))
}
return {
watch,
/**
* Get the current value
*/
get value() {
return current.value
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.