-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Description
Describe the problem
Within my codebase, I always prefer singletons over passing instances via contexts, this is mostly because these singletons are not something that is expected to change or be different over the entire session, and in the testing side I also believe more in E2E testing more so than individual component testing.
I'll be deferring to Solid.js here as that's something that I've been using often recently, and so I'll be posting snippets of my Solid.js implementations of stuff.
For example, persisting to local storage, I have this implementation for persisting an object to storage. I instantiate this directly on a global module to be imported directly by components/routes that needs them, used to contain app preferences like app theme, currently active accounts, filters, etc. Since the object does have to be persisted, there would have to be a stray effect being created for this.
I also have an implementation for listening to media query changes as a signal that can be read by any effects/components at any time, and by using these two implementations, it would make sense that I would also have a stray effect running outside of components so that my app could choose the appropriate theme to use for the app itself.
Describe the proposed solution
Of course, allowing effects to be run outside of components as is would be a bad idea, it could very much be unintentional, so one way we could do this is to introduce a $root rune that would permit these effects to be ran.
let count = $state(0);
$root(() => {
$effect(() => {
console.log('count is', count);
});
});
count = 2;$root could return with a function that would allow for the newly created effects to be destroyed
const destroy = $root(() => {
// ... effect
});
destroy();or alternatively, we could just pass a destroy function to the callback being called, and $root would return any value from that callback, I think this would be neat if one would like to instantiate everything within said $root, this is what Solid.js also does.
// Let's have a persistent counter
const instance = $root((destroy) => {
let count = $state(0);
$effect(() => {
// persist the count
});
return {
get count () { return count; },
set count (next) { count = next; },
destroy,
};
});
counter.count = 8;
// We don't need it anymore, goodbye!
counter.destroy();Alternatives considered
The alternative would be to just avoid creating these stray effects, yes, I could just instantiate these helpers, or parts that are necessary for my app, inside components directly, like in the main Svelte component and have it be passed via contexts. But I still don't think these are something that necessitates being passed via contexts.
Another alternative would be to allow effects to be ran outside of components directly, but this is an idea that even I don't agree with, and I think everyone in the dev team also does which is why the error is even there to begin with.
Importance
would make my life easier