[HN Gopher] Show HN: A tiny and fast reactive observables librar...
       ___________________________________________________________________
        
       Show HN: A tiny and fast reactive observables library via functions
        
       Author : rahim_alwer
       Score  : 59 points
       Date   : 2022-06-26 13:57 UTC (9 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | tobr wrote:
       | Maybe worth mentioning it's JS/TS.
       | 
       | Does it handle subscribing to individual values in a collection,
       | or adding/removing values?
        
       | fabiospampinato wrote:
       | Nice! I love these tiny libraries. Some random thoughts:
       | 
       | - I like that the effect function returns a disposer.
       | 
       | - I don't entirely understand what it means to dispose of
       | observables, is that just an internal optimization for cleanups
       | basically? Exposing an API for this feels a bit risky, like it
       | feels easy to misuse.
       | 
       | - Batching everything feels interesting, no "am I in a batch or
       | not?" problems anymore, though I quite like when things are just
       | executed immediately, I personally prefer to opt into batching
       | only sparingly and for performance reasons.
       | 
       | - A function for creating roots seems missing, I think that's
       | important.
       | 
       | - "isComputed" feels like a weird function to have, maybe it
       | should be called isReadonly since there's a readonly function too
       | and that's what the computed gives you basically? Also maybe
       | there should be an "isObservable" function too?
       | 
       | I've made something similar myself, also inspired by Solid and
       | Sinuous, it started as a fork of Sinuous' observable actually:
       | (https://github.com/vobyjs/oby).
        
         | brundolf wrote:
         | > I don't entirely understand what it means to dispose of
         | observables, is that just an internal optimization for cleanups
         | basically?
         | 
         | In MobX there are various things that have to be manually
         | cleaned up at different times to avoid memory-leaks, since
         | reactive systems like this involve a lot of cross-references. I
         | could imagine eg. that cleaning up an observable in this
         | library releases references to all memoized values that were
         | derived from it
        
       | jbreckmckye wrote:
       | You might also be interested in my microlibrary trkl.js, a
       | Knockout-like observables library that's just 383 bytes minified
       | and gzipped:
       | 
       | https://github.com/jbreckmckye/trkl
       | 
       | I've used this for production sites where bytes down the wire is
       | a hard constraint.
        
       | kevinfiol wrote:
       | Nice!
       | 
       | Add me to the list of people who've made their own "tiny"
       | observable js lib (https://github.com/kevinfiol/vyce). Albeit,
       | this one looks more complete.
        
       | rahim_alwer wrote:
       | Hey everyone! I put together a functional reactivity library that
       | uses observable functions as it's primitive for tracking state
       | and changes. It's super tiny (850B), fast, only re-computes the
       | dirty parts of a computation tree, batches updates via a
       | scheduler on to the microtask queue, and works in both browsers
       | and Node. Love any feedback :)
        
         | fabiospampinato wrote:
         | Do you have an implementation of the cellx benchmark? [0] It'd
         | be interesting to see how it will perform there.
         | 
         | [0]: https://codesandbox.io/s/oby-bench-
         | cellx-s6kusj?file=/lib.js
        
         | leeoniya wrote:
         | $a(); // read       $a.set(20); // write (1)
         | $a.update((prev) => prev + 10); // write
         | 
         | why not $a(20) and $a(prev => prev + 10)?
        
           | MH15 wrote:
           | I agree, this would make it a lot cleaner.
        
           | fabiospampinato wrote:
           | It may be as a safety measure, it's much less likely to
           | misuse the setter if it's divided into two different
           | functions. If it's just one function you need to constantly
           | ask yourself "wait, am I setting a function or not?", unless
           | you always want to write `$a(() => value)`, which would be
           | pretty ugly.
        
       | danielvaughn wrote:
       | So I just started a new side project, and I'm using JS without a
       | framework for the first time in over 5 years.
       | 
       | I poured over several state management libraries before just
       | deciding to write a naive solution on my own. I've got a very
       | simple stateful class - the data model itself is private, and it
       | has a custom getter/setter for access. The setter broadcasts a
       | custom event for each top-level key that changed.
       | 
       | Super simple, easy to understand, and the limitation forces me to
       | keep my state as shallow as possible.
       | 
       | Am I missing something? I expected to run into trouble but so far
       | it's been easy breezy.
        
         | wlib wrote:
         | I made a library in which state management started the same
         | way. A simple approach is great for performance-sensitive parts
         | and it's easy to write, but there is very little helpful
         | structure to it. Reactivity fails very easily, consider a
         | dependency graph like this: a -> b a -> c b,c -> d In this
         | case, updating `a` would cause d to be calculated (and
         | reactions run) twice. Also, update batching is absolutely
         | necessary for anything non-trivial.
         | 
         | At least for what I made, I don't think it's too complex for
         | what it provides. It transparently integrates with the event
         | loop and only runs what it needs to, at the cost of (min-
         | brotli) 432 bytes at the moment. Compare the "naive"
         | SimpleReactive with the complete FunctionalReactive version
         | here: https://github.com/Technical-
         | Source/bruh/blob/main/packages/...
        
       | [deleted]
        
       | junon wrote:
       | So... S.js[0]?
       | 
       | https://GitHub.com/adamhaile/s
        
       | rawoke083600 wrote:
       | Looks interesting...
       | 
       | One critique is "light/tiny/lightweight" whenever I read about
       | software being "light" I cringe and get an eye-twitch(ok not
       | really). But adding "light" or claiming your product is "light"
       | means usually nothing other than its _new_.
       | 
       | Sure it might _start out_ as small /lightweight but usually this
       | is because the software is _new_.
       | 
       | Once you start adding in corner-cases (the ones you haven't even
       | thought about) some iterations of bug fixes (hey we all human
       | programmers in the end), mix in a good dose of pull-request from
       | helpful contributors (User:'Your library looks great, if you
       | accept this PR it will fit _my needs_ perfectly. ') and finally
       | bring it up to par with a competitor of two (over time of course
       | !).
       | 
       | Your now _light_ software is no longer _light_ !
       | 
       | What is the answer ? To be honest I don't have one, but to say
       | knucckle down and be sure to say no often enough and have a laser
       | focus on the _exact problem_ your software is solving. Of course
       | it seldom the case in real life with real software :)
       | 
       | Anywhoo ! Congrats on actually releasing something :)
        
         | christophilus wrote:
         | Preact, Mithril, Zod, etc. There are plenty of projects that
         | have remained relatively light by saying "no" repeatedly to
         | things which are outside their scope or would introduce
         | unwarranted bloat.
        
         | fabiospampinato wrote:
         | I'm not sure that's really true for something like this, I've
         | been working on something similar myself in these past few
         | months, it got to about 5kb min+gzipped with all things
         | included, and there's _a lot_ included, but if you only include
         | the functions provided by this tiny library I wouldn't expect
         | it to be much much larger than it, maybe a 2x or something?
         | That's still small, fixes and performance optimizations tend to
         | cost something, but you will never have to pay 10kb to use
         | those 6 functions basically, the rest will be tree-shaken off.
        
         | a1371 wrote:
         | This is only 850B, to me that was useful info that was
         | mentioned in the headline. Yes it might get larger in the
         | future or it might not.
        
       | pyrolistical wrote:
       | I don't understand the purpose of effect. Seems like compute can
       | be used in all cases since you can dispose compute as well.
       | 
       | Couldn't read only be replaced with wrapping an observable in a
       | closure?
       | 
       | const $a = $observable(42)
       | 
       | const $b = () => $a()
       | 
       | IMO "use after dispose" should crash to make it easier to detect
       | bugs. Also dispose should error when it texts a request to
       | dispose something that still has a dependency. It is unlikely
       | this is intended as it would likely lead to a "use after free" or
       | memory leak.
        
         | fabiospampinato wrote:
         | > I don't understand the purpose of effect. Seems like compute
         | can be used in all cases since you can dispose compute as well.
         | 
         | Not OP, but:
         | 
         | - You could just use computeds instead of effects, but:
         | - Effects don't have an internal observable, so they are
         | cheaper to make and to keep in memory.            - If OP will
         | implement something like Suspense they'll probably want to
         | pause the execution of effects, but not the execution of
         | computeds.            - OP didn't implement this, but
         | potentially you could support returning a cleanup functions
         | inside effects, you can't do the same for computeds.
         | 
         | > Couldn't read only be replaced with wrapping an observable in
         | a closure?
         | 
         | Yes, internally it's probably just a `$computed( $observable
         | )`. The differences are that one is just a function and the
         | other is an observable, which is a detectable difference that
         | may matter, but also potentially the utility function could
         | return you always the same observable if it has already a
         | reference to a read-only version of the one you give it, which
         | may consume less memory (1 function to keep around rather than
         | N), though this is a bit of an edge case.
        
       | afranchuk wrote:
       | Cool! This reminds me of S.js [0] which I've used a decent amount
       | to great effect, but it seems about half the size. I'll have to
       | look at how they compare (though if someone knows off the top of
       | their head that'd be appreciated). S.js is nice because it has a
       | helper library (surplus) for dom things.
       | 
       | [0]: https://github.com/adamhaile/S
        
       | [deleted]
        
       | damsta wrote:
       | Thanks for sharing! Looks great!
        
       | cjblomqvist wrote:
       | Another notable reactivity lib is Vue's reactivity parts.
        
         | amitp wrote:
         | Vue's is especially nice in that it handles deep and imperative
         | updates.
         | 
         | For example: you have an observable foo, and it has an array
         | bar. You can read foo.bar directly instead of needing a wrapper
         | getter function. You can also update foo.bar = ... instead of
         | needing a setter function. You can also call foo.bar.sort() and
         | Vue will pick this up.
         | 
         | I see lots of small reactive libraries but a lot of them will
         | not pick up something like foo.bar.sort().
        
       | p2hari wrote:
       | Surprising no one mentioned here about
       | https://github.com/staltz/xstream Creaed by Andre Staltz, the
       | creator of cycleJS and other interesting reactive libraries in
       | JS. xstream is powerful and also much smaller in size. Give that
       | a try.
        
         | fabiospampinato wrote:
         | Much smaller than ~800B? It looks like ~5kb min+gzip according
         | to bundlephobia
         | (https://bundlephobia.com/package/xstream@11.14.0).
        
       ___________________________________________________________________
       (page generated 2022-06-26 23:01 UTC)