Lazy-initialized createMemo's updating once, but not again
I'm trying to debug this
createMemo
issue I'm running into. What I'm really trying to get at is a nested memoized object, but given that something like that doesn't appear to exist in the API as is, this is how I'm doing it. I threw together some sample code (I'll leave it as the next comment) to show a minimal example of the issue I'm running into.
When you click either of the counter buttons, the count increments, and the fibonacci memoized value for that counter updates. But then any subsequent increment, the fibonacci value does not update. So it appears that the memoized computation performs properly for the initial state, and for the 2nd state (which implies that the dependencies are being inferred properly). But then it just stops working after that.
In the real-world thing I'm working on that inspired this sample, there could be thousands of items (counters in this example), and only one might be looked at at a time. So I'd like for the memoized computation to be lazily initialized when needed (so that 1000 computations aren't kicked off when they're not needed), and for the memoized computation to be remembered if the view changes to another "counter", and then back to a previous one, and so that the memoized result can be shared between different views that have access to the memoized computation.
Would love to understand why I'm seeing this odd behavior of the memoized computation working for the first 2 states, but then not after that.
Thanks!19 Replies
Sample code:
mm
a bit unusual codestyle with the memo inside the function
not sure it is actually doing anything
the bug has something to do with cleanups, but don't really understand why tbh
but maybe https://playground.solidjs.com/anonymous/6b3c6ee7-ea9d-41e7-b187-312752898d4b is the pattern you are looking for
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
as in even if it would work i don't think it would actually be memozing the function since you recreate the memo-function with each time you call
getFibForCounter
Thanks for looking into this @bigmistqke !! I'll try out your alternate patterns for now. But I am still curious about why this other pattern isn't working.
You're saying that the memo-function is recreated each time
getFibForCounter
is called, but I don't believe that's true. This is how I'm interpreting what's going on:
- getFibForCounter("a")
1st call: the memo-function is created and stored in fibByCounterKey["a"]
because fibByCounterKey["a"]
isn't defined yet.
- getFibForCounter("a")
2nd call: the memo-function that was created in 1st step is retrieved from fibByCounterKey["a"]
because fibByCounterKey["a"]
is defined now.
Am I missing something? Thanks for helping me understand! Would really love to get why this pattern isn't working!
I guess I'm really wishing there was something like createMemo
but parameterized. Like, in the way I could make a parameterized derived signal like this (I know that's technically not an Accessor
, but it still works right):
I wish I could make a memoized parameterized derived signal, where it would key its memoized values by the parameters.
And specifically I'd like to understand why the original code appears to work for initial state AND 1st update, but not any subsequent updates. I could rationalize it away if it didn't work for any updates, but it just seems odd that it stop after the 1st update.Oooh ok so going off of my first code sample, here is a slight alteration of it where I call
getFibForCounter("a")
in the Component function for the "a" counter, but not the "b" counter. So this initializes the memo-function for "a" before the return statement, but not for "b". And now, it works for the "a" counter, but not for "b".
https://playground.solidjs.com/anonymous/8d6d68ec-60b5-4ca5-b4a3-7081d33badbaSolid Playground
Quickly discover what the solid compiler will generate from your JSX template
So I wonder, is there something incorrect about using
createMemo
inside a reactive frame (I don't know what to call this)? Like it seems like the memo works correctly when initialized in the Component function (during setup as opposed to during render I guess?). But is acting strange when the memo is initialized in a reactive render?
But if that's the case, I would expect Solid to yell at me for doing that, the same way it yells at me for creating computations outside of a render context. Should Solid be able to recognize like "hey, we're in a context where using createMemo doesn't work, here's a warning", the same way it can when you're not in the proper context?I wish I could make a memoized parameterized derived signal, where it would key its memoized values by the parameters.yes, you can at least mapArray and indexArray can provide it for you for arrays and you can twist it to work for objects too
You're saying that the memo-function is recreated each time getFibForCounter is called, but I don't believe that's true.ye u right missed the cache part
So I wonder, is there something incorrect about using createMemo inside a reactive frame (I don't know what to call this)? Like it seems like the memo works correctly when initialized in the Component function (during setup as opposed to during render I guess?). But is acting strange when the memo is initialized in a reactive render?ye it has something to do with cleanup, but i am not 100% sure why
interestingly enough: https://playground.solidjs.com/anonymous/0435b84d-83db-4982-81eb-9962e9ec801b this works
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
🤷♂️
WHOA! So just returning the accessor instead of the unwrapped value from
getFibForCounter
, right??
Ok I LOVE that that works, but I can't comprehend how it works....?? I mean it's literally doing the same thing, just moving the unwrapping up to the caller. Right?? What am I missing here?All right I've got another one to throw at this quandary! https://playground.solidjs.com/anonymous/13b9938d-f7b5-4227-9b58-f32c58d0c272
I set up A and B the exact same way, and log the result of the lazy memo for each in a
createEffect
. The only difference is I also render the result of the lazy memo of B in the DOM.
The result is that the effect for A only triggers on the first update, and not on any subsequent update. But the effect for B triggers on every update.
My expectation is that A's effect should also trigger on each update, so I'm wondering why that's not happening??Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Here's a simplified version using 2 signals instead of a store: https://playground.solidjs.com/anonymous/7ff5df2b-02a5-4385-a669-d4bc44d2c99f
Same issue as before. And also something interesting (happens in both versions), for B, the bMemo and bLazyMemo effects seem to alternate which order they get triggered in on each change. Not sure why, but that could point toward the reason for this strange behavior.
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
ye interesting puzzle 🤔
+ there is also https://github.com/solidjs-community/solid-primitives/tree/main/packages/memo#createlazymemo that could be of interest to you
GitHub
solid-primitives/packages/memo at main · solidjs-community/solid-pr...
A library of high-quality primitives that extend SolidJS reactivity. - solidjs-community/solid-primitives
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
ok so i think i know why it doesn't work: https://playground.solidjs.com/anonymous/1ee6512d-a859-4cd4-926f-87a9465ef710
you see when you press it how memo is cleaned up twice?
that's because, while the declaration is happening outside of the effect, when you execute the memo, you are doing it inside the
createEffect
.
I am going to give a bit of a botched explanation, so bear with me.
In solid there is this concept of ownership. It's a mechanism that is used to automatically cleanup effects. It's quite central to solid as a framework, but not super documented. There was a nice read-up by someone in this discord, i'll see if i can find it.
So, because that memo is executed within the effect, it is being cleaned up by that effect the first time it is being run. That's why it is being run the first time, but doesn't update afterwards.Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
There was a nice read-up by someone in this discord, i'll see if i can find it.found it: https://special-page-159.notion.site/Solid-Deep-Dive-7fd2d047d54a4e3ea60b4c2597869286
Oh man this is awesome, thank you for explaining and digging that up! I'm gonna take some time tomorrow to try to digest this. Thanks also for pointing me to
createLazyMemo
and the Solid Primitives project, didn't know about that!!