Hacker News new | ask | show | jobs
by makkesk8 814 days ago
Looks useful, but what baffles me is.. Why is every framework setting state or their "signals" using "setX" functions? What's wrong with the built in getter and setters that you can either proxy or straight up override?

This feels arguably cleaner: something = "else";

Than: setSomething("else");

6 comments

Some libraries that feature signals style reactivity do use getter and setters (ember.js, and mobx are 2 good examples). However it makes sense for the primitive API to use functions since getter and setters are functions under the hood and get applied to an object as part of a property descriptor. It's also not always desirable to have a reactive value embedded in an object, sometimes you just want to pass around a single changeable value.

As for why some libraries choose the `[thing, setThing] = signal()` API (like solid.js) that's often referred to as read write segregation. Which essentially encourages the practice of passing read only values by default, and opting in to allowing consumer writes on a value by explicitly passing it's setter function. This is something that was popularized by React hooks.

Either way this proposal isn't limiting the API choice of libraries since you can create whatever kind of wrappers around the primitive that you want.

    something = "else";
That is not observable for the primitive JS types that aren't objects and have no methods or properties or getters/setters (string, number, boolean, undefined, symbol, null).

    some.thing = "else";
The `some` can be proxied and observed. Most frameworks are setting up the `some` container/proxy/object so everything can be accessed and observed consistently. Whether the framework exposes the object, a function, or hides it away in a DSL depends on the implementation.
One big problem is right now they are _tremendously_ slow to use. (At least through the Proxy native class). Not sure if this is an artifact of JITs or the nature of prototypal inheritance.
Because javascript lacks scope-as-an-object. You can't track `var x; x = value` through it. `setSomething()` sends a notification after an assignment. Also, DOM elements can only take raw values and must be manually updated from your data flow.

Adding these features in-browser would seriously slow down DOM and JS and thus all websites for real. So instead we load megabytes of JS abstraction wrappers and run them in a browser to only simulate the effect.

For one I can write ".set" and the IDE would auto-complete with all possible somethings that can be set, even without having the slightest idea of which ones there are.

I've very much enjoyed this kind of consistency wherever is found (having a common prefix for common behaviors, in this case, setters)

well to use setters it has to be "foo.something = else", because JS can't override plain old local bindings -- not since "with" was sent to the cornfield anyway. Once you do that, you can indeed have a framework that generates getters and setters, which is exactly what Vue 2 does. Switch to proxies instead of get/set and you have Vue 3 -- the signals API is pretty much identical to the Vue composition API.