Open Nefsen402 opened 1 month ago
The signals as proposed here cannot support these features and I don't think they ever will
Why do you feel this way? :thinking:
I think we could implement an Observer
and OObject
structure based of the current proposed implementation as well as the utilities prototyped over here: https://github.com/NullVoxPopuli/signal-utils
I'm not sure which exact feature would be unimplementable here 🤔 You can deeply proxy plain objects in userland, turn off automatic tracking with untrack
, listen to all properties in one of these state objects cheaply etc.
The existence of some "global" doesn't seem like a big deal since that's an implementation detail.
At the end of the day just because a proposal exists doesn't mean it has to be adopted, if you don't see a reason for doing that. If you think the current proposal is too limited it would be worth clarifying what's one thing that can't be implemented on top of it that you think is important enough to have.
My concerns were mostly about the concept of deltas being propagate through the library. A main feature is to be able to nest Observables
to create one reactive mega object.
const user = OObject({
projects: OArray([
createProject(),
]),
profile: OObject({
name: '...',
password: '...'
})
});
user.observer.watch(delta => {
// these deltas can be synchronized to the database or whatever
console.log(delta);
console.log(delta.path());
});
user.observer.path('profile').ignore('password').watch(delta => {
// we also want to synchronize with the client, so we can create a signal that listens on a subset of the state tree
})
user.profile.name = "new name"; // the watcher will log: {value: "new name", prev: "..."} with the path ["profile", "name"]
My concern isn't that the current proposal couldn't add some way to pass arbitrary auxiliary state through the effect()
method, my concern is whether we really should given the basis that so called governors
- the things that control what part of the state tree a signal cares about are also a fundamental feature of the library. I believe this complexity should stay within a library and should not be part of JavaScript.
There is no one way of doing signals. It seems that the popular way of doing signals these days is by relying on global state, however a more explicit model could also be used. As an author of a library that has a concept of signals and that it is used in production, I have some notes about this. I originally developed destam to implement real time collaboration between clients. This library was originally born out of the concept of wrapping regular JavaScript Objects and Arrays into proxy objects to add reactivity. Initially, there was no concept of a signal, you could subscribe to these proxy objects directly. However, as the library matured, I concurrently developed signals without knowing I was developing signals. This means that the API that I have for my signals is actually quite different than the ones being proposed here, and in fact are fundamentally incompatible. If this ever gets introduced into JavaScript, this library cannot use signals as they are in this proposal. Let's talk over some of the differences to compare and contract:
Signal creation
Signals in destam (they are called Observers in the library) can be created in multiple ways. The way that most people would be familiar is
Observer.mutable
which is just creating an observer that can be mutable. On the flip coin,Observer.immutable
is a version that cannot mutate. Observers can also be created from a so calledObservable
. Observables are these proxy objects I've been talking about and they can be used to generate signals/observers.For example
In this case, we also have other features to sort of "drill down" and precisely extract the information we are talking about. The nameSignal will not be bothered if the counter state has been changed and vice versa. Note that
state.observer
is already itself a destam signal. Destam signals themselves provide these query features such aspath
and other. These can get so much more complex. Please see destam state trees for more information about this.Computed signals
Destam tries to use global state is little as possible (there is one function that uses global state to atomically manage multiple observers and when events should be fired - but I wish to refactor this). For computed signals, no global state is used to track what the dependence for the information are, this is all explicit.
The most basic way to create a computed signal is by using the
map
method. Map can also be used to go backwards:The most basic way to create a computed signal is by using the "map". We can also depend on multiple pieces of state using
map
in conjunction withObserver.all
:This signal will combine the
count
state and the namestate
which will be (lazily) updated when either of the dependent states are changed.This may be a little bit clumsy to some, but the reason why we did it this way is because this allows for a variable amount of signals to map over. If the argument sent to
Observer.all
is itself a signal that resolves to an array, we can have a variable about of signals.Takeaway
The signals as proposed here cannot support these features and I don't think they ever will. There is also this whole world that destam implements called deltas (which is the basis of the said collaboration feature) and those are also fundamental to signals in destam. Since I developed these signals in mostly isolation without realizing it, I have come up with an API that looks quite different than what is popular. But things were also designed in this way to also support deltas and state trees. This extra complexity that is required by destam probably isn't also a great candidate to actually just throw into the proposal.
I don't think that signals should be part of JavaScript as the way they stand currently, or ever. This will benefit a few simpler state models, but not satisfy more complex ones. I don't think this should be part of JavaScript as a first party feature.