createEffect vs createEffect + on
What could be the reason that the first
createEffect
is triggered but not the second one using on
? I feel there is something obvious I'm missing. Using solid-js 1.9.5
This is the output from the first console
call:
The prop is a property of a store if that matters:
18 Replies
Any chance you could replicate it here?
https://playground.solidjs.com/anonymous/d72eb037-7569-4225-b447-3faba1f7d58b
I'm wondering whether it has something to do with how you modify the store.
The first version will subscribe you to changes to anything along the path of the proxy necessary to get access to the final value.
The second version will run the dependency function under the same circumstances but I believe that the effect function will only run when the
config
object reference changes.Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Thank you for setting that up for me, @peerreynders. I tried to replicate it by using pretty much the setup I have but both
createEffect
executes here: https://playground.solidjs.com/anonymous/f6b82b77-944c-4aaa-9ef5-7f8cf3728864
Either I accidentally fixed something when replicating or there is something else in my setup that's causing it. It's a rather complex GUI application with a lot of signals. I'll keep investigating and will keep you posted.
Thanks again for the help this far!Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
@peerreynders Oh, I think I found something now, even though it does not make a lot of sense.
I have two different socket.io message handlers where I set the value of
nearestPointConfig
. If the first one updates the value, I get both "effect" and "effect 2" outputs but if the other one is doing the updating, I get only the "effect" output. The values I set look no different from each other.@peerreynders Okay, I managed to replicate it now: https://playground.solidjs.com/anonymous/18509665-99be-4412-8209-3cb410f8a3c5
(updated link)
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Hmm, it doesn't seem to happen in your playground so you might be on to something with having to do with how I update the values. If you get the chance, please take a look and see if there's something that is odd with that or the way I set up the context.
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
console.log does more than a prop access
@zulu Wow, thanks! For me it seems totally counterintuitive to do
{...props.nearestPointConfig}
, how did you come up with it?spread operator
in this case the spread is like a short cut for iterating over the object or even array keys/values
top level
MDN Web Docs
Spread syntax (...) - JavaScript | MDN
The spread (...) syntax allows an iterable, such as an array or string, to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected. In an object literal, the spread syntax enumerates the properties of an object and adds the key-value pairs to the object being created.
also console.log in effects might need a warning in docs it is a source of discrepancies in the learning process
https://discord.com/channels/722131463138705510/722131463889223772/1359995272637452378
@zulu Sorry, I meant destructuring was counterintuitive and was wondering how you came up with that as a solution
Perhaps not the right term here but hopefully you understand what I meant : )
I just know the destruct access/read each key in the object you destruct, so each key is tracked similar to what console.log does. where it access all keys
is that what you meant ?
Yes! Thank you for your help : )
also here is some visualization of the behavior
https://playground.solidjs.com/anonymous/617b414c-f832-4284-a0e5-cf53b8a1f732
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Thanks for the example @zulu! I now understand why using
console.log
on a structure is not the best choice for debugging this : )
However, if you don't mind, what makes one update different from the other in my case? It seems I update the store in exactly the same way two times but only one works for both. Given your example, I could see my second createEffect
never working but sometimes it does, which confuses me
What is the takeaway here? That when I use a store, I should always track things by the leaves of the tree to be certain it works?
I've recently implemented this global store to avoid passing props everywhere so in some regards it is different to how I use stores in other places. Perhaps I've just been lucky this far : )If you make the following change:
You'll see this:
The first timeout replaced
undefined
with an entirely new object—therefore both effects fired.
On the second timeout the first effect doesn't actually see a new object reference; it's identical to the last one—what it is reacting to is that the “guts” of the object have been replaced.
The second effect only specifically is subscribed to changes to the object reference. Given that the object reference doesn't change on the second timeout the effect doesn't run.
The thing that tends to be overlooked by newcomers is fine-grained reactivity. fine-grain change will not trigger coarse-grained reactivity. The way tracking works, you only subscribe to the exact value that you actually access.
“You accessed this value last time so I'll run you again the next time that value changes”. With primitive values it very clear when the value changes.
When it comes to objects and arrays however “the value” is the object reference. If you don't explicitly access their content under a tracked context then Solid's tracking doesn't know that you are interested in changes of the content.key points:
1
when you set an object with another object using the store setter. there is a shallow merge
if you create an effect on
store.a
setting the store.a
like we did above
will not trigger. ( because the setter preforms a shallow merge which does not replace the value of store.a
only a value change in the a
key, will trigger a tracking of store.a
2
spreading allow the effect to track not just existing keys, but also new keys that may be added in the future.
for example
if we set a new value in store.a
now
we will see the effect rerun and print
"spread store.a"
there is still a shallow merge, but the ...
force solid to track changes to the object itself.
in other words it tracks all top level changes to the object we destructed/spread
3
setting a key of a store with an alternating types
object TO non object {} => ""
or
non object TO object null => {}
will trigger the effect, because there is no shallow merge
between object and non object value like ( 1 and {} can not be merged )
so here the value of the key is replaced and the
effect will re run
Thanks for the answer, it makes sense now. For me, the problem is probably not so much that I don't understand fine-grained reactivity (I've been using solid-js for a long time but still have many knowledge gaps). It is more that I don't know what I don't know in this case. I could very well have been blissfully unaware and lucky so things have just worked – and I've drawn the wrong conclusions from it : )
With signals, I think types such as I have one other case where I have a type
so there is something I'm still missing with the shallow merge. Really sorry if you've already answered it and I've missed it or not understood it.
{ names: string, areas: AreaType[] } | undefined
trigger effects when set and there is no deep, nested reactivity to consider. I hope : )
What I didn't like using lots of individual signals was to prop pass those around as much so I wanted a context and I chose a store for it. I figured as long as I always replaced e.g. store.a
as a whole, I would get the same behavior as the previously used signals. Which does not seem to be the case. I need something in between a signal and a store : )
@zulu Thanks a lot for the detailed reply. It looks like the spread does exactly what i need, i.e. one level of reactivity – no more, no less.
Slightly OT: what I haven't yet figured out is how to get reasonable code using on
with the same construct:
Since { ...undefined }
gives me {}
, I get an object with all members optional, which is not exactly what i want.
It might seem like a weird design (and it could very well still be : ) The solid-js app speaks directly to an embedded device without internet access so for the most part there is one server and one client. The app provides real-time control for the operator and many of these messages arrive around 30 times a second.
[Edit: See next message] Still, what I don't understand is why this only breaks in this specific case. I've got other places where I do the exact same thing and it seems to work fine. Is the underlying problem that I set the value in two different places?
That is,
and then
works as expected. I'm still not sure I understand when it works and when it does not work.
That is, I think I understand what's going on in the original case and the explanations make a lot of sense. Reading it, I would have thought that the above example should only trigger the effect when going from undefined
to an object but not when replacing the object.
Oh, targets
is an array though. Perhaps that's the thing.
I think I need to find all places where I trigger effects with on
or similar where the effect is dependent on e.g. store.a
and where a
is an object
. It might be just this single instance and that's why I haven't run into this earlier.
Edit: No, it turns out to be different (I intercept it during prop passing and use a different signal downstream) – I think I have this down now. Thanks everyone : )
{ max: number, min: number } | undefined
where I update the store as originally posted. Which seems to trigger the following effect every time: