[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)