window.dispatchEvent(new Event('counterChange'));
And every part of the application that wants to react to it can subscribe via window.addEventListener('counterChange', () => {
... do something ...
});
Anything wrong with that? window.dispatchEvent(new Event('counterChange'));
And every part of the application that wants to react to it can subscribe via window.addEventListener('counterChange', () => {
... do something ...
});
Anything wrong with that?Event handling is getting messy very easily. If you want to get deeper into it, have a look at event bubbling and propagation.
Large applications need a robust event handling. This is the nowadays hidden benefit of frameworks like Angular, Vue etc.
Believe me, you don’t want to use the standard event handling API without a framework. Adding, deleting, cloning, firing, removing, fire once etc on many elements can have serious unwanted side effects.
Being charitable, the best I can imagine right now that'd cause memory leaks is someone running into the old school JS scoping issues and capturing something in handlers that they shouldn't. That's not the handler itself that's the problem, though - that's the developer.
(Yes, we could rant on and on about the poor design decisions that JS has built in, but that's been beaten to death)
For other event listeners, they get removed when the DOM element is removed.
The frameworks do not solve this to any greater degree. They also just make everything invisible and behind-the-scenes and hard to debug due to their declarative nature, but that is another topic.
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
In this example, the event is on the Window. There is no bubbling. It is already at the top level.
>Believe me, you don’t want to use the standard event handling API without a framework. Adding, deleting, cloning, firing, removing, fire once etc on many elements can have serious unwanted side effects.
I don't know what this means. The frameworks do not have much to do with this topic.
Frameworks batch changes so that updates are efficient, and in many cases figures out the "correct" order of doing things. If you do all of those yourself in a large and complex UI, very likely you are updating the DOM less efficiently than what frameworks are doing, and very likely you introduced some subtle bugs. Speaking of that from my first-hand experience.
The difference with signals is that the resulting value is only ever calculated when the end consumer reads the value- so you schedule render updates asynchronously from the actual writes to the signal, and whatever chain of computations the watchers perform is done just the one time during the render.
Interim values sent to the signal will get lost, so you really can't do too much interesting work in them. It's really just a fancy abstraction layer to coordinate a rendering cycle.
It can also be more performant, eg, say you have a computation that depends on 2 values:
`result = a ? b : 0`
Then if a is falsy, we don't need to recompute if b changes. This is achieved automatically with signals, but would require quite some code with classic pub/sub.
And it's hard to ensure that all listener don't cause that trigger cascade.
In fact, the GOF spend an entire paragraph on the problem of complex update semantics in "design Patterns" (1995) ch Observer p299.
So, while it is a real problem, it's one that has been solved (for at least 29 years)
When the app is small you likely don't need it. And then it grows and you absolutely do.
It's better, in greenfield to use framework/tooling that is ready for it.
> It's better, in greenfield to use framework/tooling that is ready for it.
I don't really agree. Tooling, and even more so, Frameworks, come with giant trade-offs. Some are "paint-in-a-corner" trade-offs. So I would caution against pulling in a framework just to solve potential future issues. So much so, that I think it is one of the top10 things that will cause your project or startup to fail or get into serious trouble. It's really a form of "premature optimization".
https://github.com/ctx-core/rmemo
Nanostores is also small https://github.com/nanostores/nanostores.
And if you only need reactivity in the browser (not server side), VanJS is small & includes reactive primitives. https://vanjs.org/
It has all the downsides of the pub/sub architecture highlighted in the proposal.
I might not be describing that well, because once you go down that road it really becomes a whole overall approach that infects the whole program (like functional reactive programming), and so it's really about how the whole flow fits together from top to bottom, and that can be very elegant.
I don't think that's the right fit for everything, i.e. in gamedev it might make more sense to just update some object's position imperatively, but for UI it tends to work pretty well.
window.addEventListener('counterChange', () => {
element.innerHTML = 20components.map(|c| c.renderHTML(newCounterValue)).join('');
});
or, you have to check the components on case by case basis on a. if the component need to be updated or not, and b. how is the most efficient way to update such component. And in TFA, if counter is changed from odd to odd, the label doesn't need to be updated.Also, multiply the number of events to the number of components can make the application go out of hand very quickly.
A alt proposal would be some kind of auto remove listener if it goes out of context
Best case scenario, it just slows down garage collection a little bit, as you're holding into a lot of references that aren't going anywhere.
On the other hand, I recall a bug in a particular version of AngularJS where component DOM nodes wouldn't get cleaned up when navigating with the router unless you manually set all of the scope values their templates used to null.
We had a data dense application with tables and what not, and you could clearly watch memory jump tens or more megabytes flipping between pages in the chrome dev tooling.
Eventually (this was a SPA intended to be used for upwards of hours at a time) the page would crash.
More often than not that’s due to a memory leak.
Well, don't events only bubble upwards? You need to know the exact element of it is not on a lower level in the DOM tree.
Events were too messy, so I wrote a small pub/sub message queue type of thing. Anyone anywhere in the DOM can subscribe to messages based on subject regexes.
Makes things a lot easier, especially when I added web components to wrap existing elements so that publishing and subscribing is done with attributes, not js.
Only if you have a reference to the object.
The reason I made up my own message queue pub/sub is because events required a lot of complexity in acquiring the correct reference to the correct object or subtree in the DOM.
With pub/sub type message queue, any element can emit a message with subject "POST /getnetpage" and the function listening for (subscribed) POST messages emits a RESPONSE message when the response comes back. This lets a third element listen (subscribe) for a "RESPONSE FROM /getnextpage" subject message, and handle it.
None of the 3 parties in the above need to have a reference to each other, nor do they even have to know about each other, and I can inject some nice observability tools because I can just add a subscriber for specific message types, which makes debugging a breeze.
It only works in browser environments.
See, for instance, https://www.electronjs.org/docs/latest/api/ipc-renderer
Unfortunately, modern frameworks really want to use their own event channels, which makes hooking a pain.
Welcome to Node.js v21.6.2.
Type ".help" for more information.
> window.dispatchEvent(new Event('counterChange'))
Uncaught ReferenceError: window is not defined $ node
Welcome to Node.js v20.6.1.
Type ".help" for more information.
> const target = new EventTarget()
undefined
> target.dispatchEvent(new Event("counterChange"))
trueThe only "benefit" signals as proposed here give you is less control over the exact dispatch pattern of the graph, for instance things like debouncing, throttling, batching, etc etc etc. Aka all the things you absolutely must have control over if you want to make something resembling a high performance application.
> The only "benefit" signals as proposed here give you is less control over the exact dispatch pattern of the graph, for instance things like debouncing, throttling, batching,
Events don't give you any more control over those properties than signals, they just require more boilerplate.