-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Svelte5: Deep reactivity is unintuitive with reactive Maps #11346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I had the same thought but I think they want to make it explicit. However you don't need to pass |
As mentioned writing I would, however, expect it to work without wrapping it in a
Though technically this is possible:
That feels like a painful amount of boilerplate to achieve the same, and it seems prone to error as you could end up with some values being reactive and some not reactive:
All this makes me wonder if this works as designed, or if it could/should be improved. |
They were originally designed with deep reactivity and changed back to be explicit because Dominik thought not having a I kinda agree with you that it would be nice to have it. |
Ah, I see, that makes sense. I do understand the reasoning, and I can certainly agree with it to some extend. It's how I feel about the entirety of Back on topic, I fear we'll end up seeing a lot of this in the wild:
It does the trick, but it leans toward writing JavaScript pre 2015, where everyone was writing the same utility functions because the base library was lacking. Do you have a reference to any discussions about deep reactivity in maps/sets? I'd love to read about the reasoning, and I was looking in #10263, but some quick searches left me empty handed. |
Would it be possible and desirable to enable deep reactivity if the |
It may be possible, but would break the current logical semantics of how classes work, since when you try doing it with
No, #10263 (comment). |
eh, I disagree - you're explicitly importing |
@ottomated I believe that the point is that if you would do this:
It would not result in a "reactive class", which would cause a lot of confusion among developers if This is the subtle difference between magic and voodoo. |
Suppose we were to change things so that values were automatically replaced with reactive proxies — what would a good opt-out mechanism look like? |
After using svelte 5 for a few projects, I've built the habit of wrapping anything that needs to be reactive in a rune of some kind. It feels a bit more magic than magical that import { $map } from 'svelte/reactivity';
const map = $map([['key', 'value']]);
// ^? Map<string, string>
// opt-out
const shallowMap = $map.frozen(); But then you would need to do An opt-out mechanism could not be something like |
Yeah that doesn't seem like an improvement frankly. For one thing what is a frozen map? It implies you can't let bar = $state({ deep: 0 });
map.set('bar', bar); This doesn't seem like a painful amount of boilerplate to me, it seems like a reasonable way to opt in to the desired behaviour.
And this is basically what makes me uncomfortable about the proposal to make values reactive by default. This... let foo = { deep: 0 };
map.set('foo', foo);
console.log(map.get('foo') === foo); // false! ...violates my expectations a bit too much. |
Sure, frozen is a bad word for it - could be
I don't think it's a boilerplate issue, I think it's an expected behavior issue. // this works
const obj = $state({ });
obj.foo = { deep: 0 };
// this doesn't - but it feels the same!
const map = $state(new Map());
map.set('foo', { deep: 0 });
I would call that a different thing than changing the public API. I would also argue that this is much weirder: const map = $state({});
let foo = { deep: 0 };
map.foo = foo;
console.log(map.foo === foo); // false! |
Names beginning with import { $map } from 'somewhere'; ...then you can also do this, which is just a recipe for silliness: import { $state } from 'somewhere'; We could say 'you can have names beginning with The alternative is to make Finally, we want to avoid the impression that there's something special about these classes —
It's not weirder. At most, it's equivalently weird, but I'd argue that big-blob-of-reactive-state is a more appropriate place for that sort of weirdness than something that, aside from causing effects to rerun on updates, is designed to exactly replicate built-in APIs. |
This makes sense.
This was my thought too.
I'm trying to reconcile my thoughts this, because at heart I agree with this as well. Here's the difference, I think: A. Some people using Svelte 5 will only use deep reactive state, because that's what they're used to in Svelte 4. When those people see B. Once people build an intuition for Edit: another note on this - I looked at the source code several times while trying to understand this at first and it doesn't avoid that impression - it uses internal methods rather than runes.
In the hierarchy of potential unintuitiveness, I think getters and setters on an object you defined yourself is higher than method calls on a class you imported, if that makes sense? Side note: I wonder if it would be possible to patch the AST i.e. |
It does, just because it's one less moving part if we don't need to run the Svelte compiler on its own source code in order to run tests etc. It would be slightly more cumbersome to implement in userland since we don't expose
The thought has definitely occurred! It would likely help in cases like #11403. Reasons not to do it:
Because of those, it's almost certainly better to just educate Svelte developers that |
Yeah, I understand why, just a note.
Yes, and that knowledge would extend to I honestly think the best solution to all of this is something like this. import { map, set, url } from 'svelte/reactivity';
const m = map.deep();
const shallow_set = set();
const u = url('http://example.com'); It solves the opt-out / opt-in issue cleanly, and it also solves a different footgun I ran into: if you refactor a chunk of code into a new component that includes a ReactiveMap constructor but forget to import ReactiveMap, you'll silently lose reactivity. In addition (I assume this is already on the roadmap), the documentation should include more of these examples, there should be more lint warnings around this, etc. |
Closing since we don't want to change this. Even something like Agree that documentation should touch on this, but that's tracked more generally elsehwere. |
Describe the bug
Intuitively, I would expect this to be reactive:
However, reactive Maps (and Sets) don't proxy their values by default. This breaks the intuition that values passed to the
$state
rune are made deeply reactive. I think even if this is out of scope for the reactivity helpers, it should be clearly documented.Reproduction
https://svelte-5-preview.vercel.app/#H4sIAAAAAAAACm2SzW6DMAyAX8WKKgEqgq7SLkCZdtyhe4GxQ0bNlAlClLidqijvvvDTrXQ95GDnsz_LiWWNaNGw7M0yyTtkGXtWisWMzmoIzAlbQh-b_qjrIVOYWgtFZSUrEp3qNYGFPVfgoNF9B8FUkmrkNYmToHOQD-xwWiToP76wpgfYwcoQJwwtdFxlYB24KF9S23-UxO9BFkYz7PEVNo1nQ5_blWBHF82WxBcl3HexcOLtETPYgMuvke2IGKQw4EG84C6CiupeGgLh-3jwRRJqTy2FN8pkbLNe58vbyfY52aIbyMXwuNlEc6SRjlrCJKlb5PpXLCZmXlcli_TvTWShylc0hId5h36zi8meJqsrUnXh9-P-7014DftP0PUH0Qg8sIy0z767H5Uo1qc_AgAA
Logs
No response
System Info
Severity
annoyance
The text was updated successfully, but these errors were encountered: