Closed FoHoOV closed 8 months ago
There's no difference between primitive and object states, in this REPL none of the effects rerun. What you're experiencing is a consequence of a workaround to ensure form properties update correctly after a form reset - i.e. this is specific to SvelteKit's form actions (and a somewhat unfortunate workaround which we'd like to find a better solution for some day).
@dummdidumm you seem to have missed the point, the actual REPL that represents my point is this one. In the linked issue there are other REPLs and SvelteLab examples that also elaborate on the requested feature as well. Assigment to self with the same object reference is not what i'm saying, assigning to the same value (different objects but same value) is what the actual point is.
basically, if we want to be notified if something is changed (change could a different value or reassignment to the same value) we MUST have an object, this will not work with primitive values. As you can see in this REPL, I would expect both of them to have the same behaviour. I see you point, primitves are immutable in JS, but can there be a workaround to make this work?
This actually works consistently, and whether that's intuitive or not depends on how you look at it.
When reassigning the object, you're assigning a new instance of that object. So the object as a whole has changed. The $effect
that does object.primitive
does not only listen to primitive
, it also listens to object
- in general, it listens to all signals along the object chain. It does that to play it save - we can't possibly know whether you want the effect to rerun when the whole object changes, or only when the property at the end of the chain changes (different people have different opinions on that). By doing it this way, you can easily achieve the desired result by wrapping the object chain in a derived. The other way around wouldn't be that easy.
@dummdidumm So in this REPL (which is also provided in this feature request and the linked issue) I've asked for other better svelte solutions. I've also listed the suggested solutions and the reasons I think they are not good (in this post). What would you suggest to be done instead? Also another question, wouldn't you be surprised that the effect in Alert is not getting called? I mean like now that I know the reason I will always have to be carefull if the prop or state is not a primitive otherwise it won't work with the same value being assigend to it, but isn't this counter intuitive? I want to know your thought process to improve myself to see it as you do so I can write better svelte applications.
Can one of the maintainers please tell me the intended way to achieve what this issue is referring to? :( currently what I do is to use String
objects instead of primitives. Take a look at this for instance while reassigning to the same "primitive" value actually reruns the effect.
setting the value here
let primitiveState = $state<Number>(0);
let toggle = 0;
const intervalId = setInterval(()=>{
if(toggle < 2){
primitiveState = new Number(1);
toggle+=1;
} else {
primitiveState = new Number(0.5);
toggle = 0;
}
}, 1000);
and passing it normaly like
<Button opacity={primitiveState}></Button>
Logs:
"primitive: 0"
2x "primitive: 1"
"primitive: 0.5"
If you want to ensure the effect reruns, introduce a variable to it that is guaranteed to change, like an incrementing counter. Your way of using a new object instance also works.
Describe the problem
In svelte5 lets say we have a state that has a primitive value like this
let primitiveState = $state("some value");
. Then we try to listen to its changes with an $effect:but if for whatever reason the
primitiveState
has been reassigned to the same value, the effect won't run. This at first was unexpected to me because if instead of primitiveState we had an object likelet name = $state({primitiveState: "some value"});
and changed it withname.primitiveState = 'some value'
we would get notified; but again if instead we get a derived state withconst derivedState = $derived(name.primitiveState
and listen toderivedState
changes we will not get notified. This inconsistency kind of surprised me. There is another thing as well, the exact same thing works with form actions! why? lets see:+page.server.ts
+page.svelte
Here when we click on the button at first
form
changes toundefined
which makes thederivedMessage
to be undefined at first and as soon the response comes in,form
has the valuestatic text
. Since it didn't get assinged tostatic text
back to back, we got notifined: instead ofstatic text
->static text
it went likestatic text
-> undefined ->static text
This is another inconsistency that I saw. For my use-case I first wrote a server side validation, which showed the errors in my form; then I called the exact same function in the client-side and it didn't work 🗡️ (now I know why). There is already a discussion about this in the comments of this bug report (at first I thought this was a bug). Please read the comments and visist the provided REPLs and SvelteLab links there to get a better idea of some other use cases.I'm gonna mark some points from those discussions here. The provided solutions (I call them workarounds actually) were these:
And my response was:
Describe the proposed solution
Primitive value changes should behave the same as objects. This reduces complexitity and improves DX and removes some GOTCHAS I faced.
Importance
nice to have