Stale read in <List><Show><List>

https://playground.solidjs.com/anonymous/ccb906d4-c9f1-437f-9f72-e94279b0e25d I am stumped, I don't know why the stale read is happening, I don't know why it's triggered by that stream of states and not others. Note that the playground doesn't render the error message correctly, here's the dev message:
Uncaught Error: Attempting to access a stale value from <Show> that could possibly be undefined. This may occur because you are reading the accessor returned from the component at a time where it has already been unmounted. We recommend cleaning up any stale timers or async, or reading from the initial condition.
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
4 Replies
webstrand
webstrandOP11h ago
Well I'm starting to think this is actually a bug in <Show> in that stale signal reads should not be an exception This appears to be caused by the ordering of memo updates and destruction. createMemos listeners are not deferred and run immediately, even if those listeners are owned by another createMemo that will destroy them just as soon as it gets time to run or maybe <Show>'s condition and conditionValue get updated, then, since the child of the inner <List> is listening to the same signal that caused condition to update, it gets to update, too. When it reads from the stale accessor, it sees the updated value of condition and an exception gets thrown
mdynnl
mdynnl9h ago
https://playground.solidjs.com/anonymous/dff1aaa6-18fe-44aa-b5f9-2e349f16d2a3 it basically boils down to recomputation of a child node that access the condition as a result of write operation that negates the condition with that assumption, that gives us https://playground.solidjs.com/anonymous/61aa97b9-7680-4f21-a7d8-8950bc0a48c9 essentially what List or indexArray helper is doing under the hood i know that it was done to detect cases like this but it isn't that obvious that this might happen in real world scenarios here, the node that triggers the error eventually gets cleaned up as a result of its reparent recomputing
webstrand
webstrandOP9h ago
I'm not sure if this is a scheduling bug, in the reactive system itself, or if its an issue with Show if it's the latter then something like
if (!s.untrack(condition)) {
let cleanedUp = false;
queueMicrotask(() => { if(!cleanedUp) throw new Error("USE AFTER FREE")})
s.onCleanup(() => cleanedUp = true);
}
if (!s.untrack(condition)) {
let cleanedUp = false;
queueMicrotask(() => { if(!cleanedUp) throw new Error("USE AFTER FREE")})
s.onCleanup(() => cleanedUp = true);
}
suppresses the error in this case https://playground.solidjs.com/anonymous/2d2e421b-7498-4a9d-9eef-f25e0265ad85 solid might have a better hook than queueMicrotask, to wait for the last tick before checking to make sure it was disposed of downside is: this secretly still does extra work, unlike the exception which terminates the potentially expensive update
ryansolid
ryansolid7h ago
I'm just going to write here to remind myself to look at this

Did you find this page helpful?