Hacker News new | past | comments | ask | show | jobs | submit login
Poor man's signals – tiny vanilla JavaScript signals implementation (plainvanillaweb.com)
39 points by Joeri 16 days ago | hide | past | favorite | 37 comments



Maybe it is just me, but the examples that should show up in the boxes are just blank, white areas.

Firefox on Android


Hi, author here. I just tried on firefox on android and it looks ok to me. What kind of adblock extension are you running?

Update: found it, it was a bug caused by ublock origin. Should be fixed now.


Yeah I was running uBlock – thanks for fixing it, works now.


Same on Firefox GNU/Linux.


Same here... Chrome on macos


all good here on macos and arc (chromium). how about the direct link to the frame? https://plainvanillaweb.com/blog/articles/2024-08-30-poor-ma...


That works. So perhaps it is a security thing, that is not done properly.


Did you try disabling AdBlock?


Good here MacOS and Safari


Take it any you want, as _signals_ or something else, what has been lacking in the JavaScript world and reimplemented many times is... functional reactive programming!

Our own take on this is https://github.com/okcontract/cells where we try to stick as close as possible to the spreadsheet analogy. We're using it to build a very complex web app and it's doing marvels.


`okcontract/cells` seems to be missing one of the two FRP primitives though: streams (of events).

One of the best FRP environment for JavaScript is/was FlapJax: https://en.m.wikipedia.org/wiki/Flapjax, which incorporates both the primitives and their operations.


It’s definitely not functional reactive programming, which needs to expose events and the history of events as first-order primitives, and usually a library of operations over the history of events.

If you simplify out the event stream primitive from FRP, what you are left with is not FRP.


Yes, as stated in the doc: "cells is a simplified Functional & Reactive (but not strictly FRP) library".

There were enough true FRP libraries around not to implement another one. We think cells is a sweet spot where we get most of the benefits of functional programming and reactive programming while simplifying the concepts from the original paper from Elliott and Hudak.


React is also FRP, only without impure ad-hoc DOM updates :)

But this pales in comparison to the world's most successful FRP application, the spreadsheet.


Rxjs, most.js and a handful of others been around for a long time.


Bacon.js already did FRP about 10 years ago.


This is not entirely like the original signals, but using event emitters instead.

I found https://dev.to/ryansolid/building-a-reactive-library-from-sc... to be a much better ‘from scratch’ example. This is by the maker of SolidJS


Isn't this just an implementation of the Observable pattern? IIRC the difference between observables and signals is that signals don't recompute the value unless any of its sources change, by using a dependency graph. Also, .effect() is just what used to be called .subscribe() in knockout.js and others.


Properly implemented signals are more complex than the observer pattern, since they have to prevent duplicating updates if more than one dependency changes. They also fundamentally change the way you use them compared to observers, thanks to automatically subscribing to updates to their dependencies and propagating the updates to the dependents. Effects resembles observers, but you would generally avoid them when using signals unless necessary.


Yeah this seems more like observables than signals. Which despite what you may hear on the interwebs are two different things.


Isn't this where we were 15 years ago with knockout observables?


While not 100% same thing, I've been a happy user of nanostores to manage state in vanilla JS.

It has `computed` too which works very similarly. On top of that, it has integrations with front-end frameworks such as React.

https://github.com/nanostores/nanostores


These are Observables, as seen in Svelte and RXJS. When talking about reactivity primitives / patterns, Observables and Signals are two of the most common / prominent ones. So in this context, they are closer to being 100% different things.


This seems a bit different from the kind of signals frameworks have, where dependencies are tracked automatically (no dependency array), and you can sort of chain stuff automatically, so for example you can have an effect that depends on 3 memos that depend on 4 signals or whatever else, and you never experience non-fresh values.

If you want to look a bit deeper into this I had written another sort of toy implementation that much more closely resembles what the frameworks are actually doing: https://github.com/fabiospampinato/flimsy


Nice! I recommend checking out mithril/stream [0], it's framework-agnostic and quite small but comes with your standard map/lift/etc. Does everything you need and with a standard, flexible interface.

I think the code itself is messy and outdated ES5, so I rewrote it in TypeScript and made some improvements to QOL, clarity and performance and use that package liberally across a lot of codebases. I'll post it here sometime whenever I push it to GitHub.

[0] https://mithril.js.org/stream.html


Ok, the EventTarget is a very cute trick and I haven't thought much about implementing signals this way. However, all "serious" signal libraries have at least computed (higher order signals), and a way to sample a value, meaning get the value from a signal without triggering its effects, and no you can't just call foo.value because this won't work with computed signals, and a way to batch multiple write calls to multiple signals, which is often implemented by swapping the effects in and out of the update phase of the signal.


Also, you probably need a root to place signals and effects in a separate scope that can be isolated from the rest of the signal tree and disposed manually, for you know when you have a SPA with multiple pages and tables and you want to be able to update those tables and reorder them and go back and forward in your browser history without it leaking memory like the Titanic. I am struggling to do this myself so not saying I have all the answers here.


Can you elaborate on the batching part for me? I don’t see it



I really don’t see the point. All changes come from events whether user, system, or network events. All you need is event handlers, which is obviously already there at the lowest level.

The rest of the stuff the article mentions like rerendering and DOM diffing are all extremely inefficient framework nonsense that is entirely unnecessary.

So, to achieve the desired result be event oriented in everything you do, keep things simple, and don’t let framework nonsense distract from common sense.


Why do I have to disable uBlock Origin to make this work?


> In case you've been living under a rock, here's the example from Preact's documentation that neatly summarizes what signals do:

> [blank box]

Okay, very cool


It takes like 1 more example to actually have “signals” that automatically wire up dependencies between computed() and signal(). Without that, what’s the point?

Rich man’s signals provide a really interesting way to structure incremental computation, sometimes called “self-adjusting computation”, without manual book-keeping elbow-grease from the programmer. Ideally, one can use signals as containers for the inputs to your program and receive a program that automatically does the minimal possible re-computation when a signal changes.

Without that, “poor man’s signals” is just “stores” from React’s 2014 era.


new keyword is out of fashion? Why?


I didn't know about that either. I find the new keyword useful as it instantly lets me know that I can expect the left-hand operator of the expression to be an object of a class, whereas cont foo = bar("value") doesn't say much about what foo is.


Examples don't work with AdBlock enabled—you have to disable it. Even after doing so, parts of the snippets are blurry and illegible.


FYI reading this on Firefox for Android and no example except the very last one renders




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: