Understanding Solid reactivity with console.log
I'm using createMemo with a console.log inside. Can you tell me why is this console.logging all the items, when I click on one?
Am I using createMemo in a wrong way? I thought only the 2 changed items would be recalculated.
I created a Stackblitz + GitHub repo to undestand this better:
https://stackblitz.com/~/github.com/hyperknot/solidjs-sandbox
This is the line with the console.log
https://github.com/hyperknot/solidjs-sandbox/blob/a19b9581595bedd7b5e7949560a7b0d0755b8fd7/src/1-simple/store/doc.js#L15
15 Replies
I'm still trying to decypher the code to understand why your memo is triggering for each and every click. A couple of things that I initially spot that might or might not be a cause of your bug:
1. the circular dependency of
uistate.js
and doc.js
is either already, or will in the future, cause you problems
2. why are you calling createRoot
? this is a helper from solid which in meant to initialize the reactive system, so normally you only do this once in order to 'mount' solid to the DOM.
aaaaah, I see why, you are using uiStore.state.activeDocID
inside of your memo. since the uistore is also a reactive store this means that your memo is tracking both the internal store of the doc itself. and also the uistore. This is also the behavior you want, because otherwise your isActive
would never update.
so to summarize, the reason you are seeing all the numbers being logged is because each and every Doc
is answering the question if it is active or not.
if you just wish to log the one or two Doc
's that actually changed than you want to add a createEffect
that logs the isActive
memo. then you would only see the numbers that did changethere is a primitive for stuff like
isActive
: createSelector
its purpose is exactly so that you don't need a memo for each isActive
would look something like
they wrap it in a createRoot
because they initialize the signals outside of the render-function/component.
to prevent that cannot cleanup computations outside of ...
warningaaaah, gotcha
although iirc that warning is only there when u have computations/memos/effects
just for my understanding than. isn't it bad practice to create that stuff outside of the render tree? I always avoid creating top level signals and such for the same reason. or at least I think it is for the same reason
it's a bad practice if you use SSR
because then you can accidentally create shared data between sessions
but if you are client-only you can definitely use it
that pattern of creating a module with a store and importing the store + actions is called a
singleton
in solidjs
i use it all the time, it's a pretty awesome way of doing global state, no hassle with contexts and stuff
this warning you can actually also just ignore if you don't need the computation to ever clean up, which would be the case with global state
but this would create a memory leak
because then an effect is made (and never cleaned up) on each click
the circular dependency of uistate.js and doc.js is either already, or will in the future, cause you problemsthis is very true tho and probably something you need to address
alright, gotcha. Guess I've never run into this due to my general aversion to global stuff. I always just opted for a context instead.
Thanks for all the answers! I'm still trying to wrap my head around it. What I definitely want to avoid is to create a 1000 roots. I think that's not good for performance, right?
Also, I understand that createSelector exists, but I'm trying to learn createMemo first.
This app is a pure Vite client side SPA.
So are you saying that even though the console.log runs for all items, actually not every item is updated?
I'm basically trying to understand how to minimise "re-renders" in Solid, even though it doesn't re-render. But it still updates DOM elements, doesn't it? So how can I minimise those repaints or DOM updates?
depends on what you mean as a rerender. like an actual render that you could make visible with devtools? or do you mean the reactivity system doing its job?
because with your memo you should only get 2 items that actually rerender, the one that loses the active state, and the one that gets it
but all your items run the memo itself to calculate if there is a change
so in this example it probably makes no sense to use any kind of advanced function. but what if it takes some computation. say it's a tree view and I need to recalculate all the subtrees. how can I make sure that only the changed items are recalculated, and not all of them?
I'm also trying to get to the root of it via devtools but it seems an even more complicated subject.
so basically I'm trying to learn what's happening behind the scenes, to avoid unnecessarily, potentially slow calculations
well, you have to calculate all of them no? the definition of isActive is if the doc's id is the same as the selectedId. so if you change the selectedId then you will need each item to check if that id is the same as theirs
what I'd do in this case is probably merge the uistate and docs and utilize
createSelector
this would then also fix your leaky abstraction and circular referencesI understand. But still, generally when trying to debug what can cause performance slowdowns, how should I approach it? Adding a createEffect() inside components and adding console.log? Or trying to figure out the logging component of the devtools package?
I always do the createEffect + console.log. But I will look into the devtools package you mentioned, I do not know of it
even the console.log you used the way you used it, uncovered work you did not expect to happen.
so even if you find a nicer way to console.log only for the effected components
you might not have discovered that the pattern has a wider impact.
OK, I'll come back when I have real life examples of work which needs memo, not just a simple equality.
The biggest thing for me would be to see what is actually recalculated and why. Are there any videos out there about debugging SolidJS reactivity?