Building blocks for reactive components
- o(bservable) is a value we can subscribe to
- d(erive) is a read-only o(bservable) derived from other o(bservables)
- c(omponent) manages lifecycle of o(bservables)
I wrote odc while developing echo.html
# code
<!-- MIT (c) m15o --> eq = (a, b, c = a?.constructor) => a === b ? 1 : c !== b?.constructor ? 0 : c == Array ? a.length == b.length && a.every((x, i) => eq(x, b[i])) :c == Set ? a.size == b.size && [...a].every(x => b.has(x)) : c == Object ? Object.keys(a).length == Object.keys(b).length && Object.keys(a).every(k => k in b && eq(a[k], b[k])) : 0 ;[O, D, C] = ((stk = []) => { const isd = r => { throw Error('obs:'+r) }, pub = (s, v, o) => [...s].forEach(f => s.has(f) && f(v, o)), kil = (obs, subs) => { subs.clear(); for (let k in obs) obs[k] = isd }, sub = (subs, val, fn, im = 1) => (im && fn(val), subs.add(fn), () => subs.has(fn) ? subs.delete(fn) : isd('unsub missing')) return [(val, subs = new Set, rv) => (rv = { get: () => (stk.at(-1)?.add(rv), val), sub: (...a) => sub(subs, val, ...a), set: (v, old = val) => eq(v, val) || (val = v, pub(subs, v, old)), dis: () => (val = 0, kil(rv, subs))}, rv) , (fn, cache, dty = 1, deps = [], subs = new Set, rv) => (rv = { sub: (...a) => sub(subs, rv.get(), ...a), dis: () => (deps.splice(0).forEach(fn => fn()), kil(rv, subs, 'D disposed')), get: () => { stk.at(-1)?.add(rv) if (!dty) return cache const prev = cache; deps.splice(0).forEach(fn => fn()); stk.push(new Set); cache = fn(); dty = 0 stk.pop().forEach(o => deps.push(o.sub(() => { dty = 1; subs.size && rv.get(); }, 0))) !eq(cache, prev) && pub(subs, cache, prev) return cache }}, rv) , fn => (...l) => { const d = [], tra = t => (t.dis && d.push(t.dis), t), obs = o => tra(O(o)), der = o => tra(D(o)), sub = (o, cb, im) => d.push(o.sub(cb, im)) return { ...tra(fn({ tra, obs, der, sub }, ...l)), dis: () => d.splice(0).reverse().forEach(fn => fn()) } }]})()