classList causes unnecessary re-runs (but createEffect or HTML text in the same spot doesn't)
I found myself in a situation where
classList
specifically seems to cause unnecessary re-runs. In the following code, createEffect
only reruns when fst()
changes (I manually examined w.fsts
). Similarly, HTML text re-runs only when fst()
changes (I only get one render for ${i}
for the element, which was updated. Yet, every single change of any element (executionState()[i]
) I get classList for ${i}
for every single of my tests()
. I don't understand why, especially considering that classList
and HTML renders are right next to each other.
10 Replies
I assembled a self-contained example trying to recreate the issue, but I didn't succeed:
runs exactly as expected without any extra re-runs for
classList
.
I was able to get the minimal example to work:
{values().join(',')} {values()[i]}
doesn't not cause trouble, but adding nearby:
<button disabled={values()[i] <= 2}>Low</button>
doesThere are some oddities currenly with
classList
, but afaik they are all about how class
and classList
are merged with each other. Maybe it has something to do with how some effects are merged in solidjs.
<button disabled={values()[i] <= 2}>Low</button> doesaha, this seems to point towards merged effects solid sometimes goes for less-then-granular as an optimization technique
I read about it, but the official page said it's ok to have
class
static. Regardless, I tried without that extra class
attribute and it doesn't change anythingI submitted a bug-report:
https://github.com/solidjs/solid/issues/2525
GitHub
Dependency leakage between HTML attributes (=Extra dependencies cau...
Describe the bug I found that classList (and possibly other HTML attributes) causes unnecessary re-runs. In the following code, createEffect only reruns when v() changes (I manually examined w.vs)....
yes, this is a quirk of effect grouping and intentional trade-off for lower creation cost
although the expression is re-evaluated, dom won't be updated unless the result actually changes
to clarify more, this happens with every attribute that isn't text or children
Thanks for a reply!
dom won't be updated unless the result actually changesYeah, but I have an array with 200-500 elements, so even if DOM doesn't update, every single attribute of every single array-element re-running for change of any element is undesirable. Now, I think I know how I can fix my specific usecase: replacing
disabled={values()[i] <= 2}
with cached disabled={v() <= 2}
solves it.
But, any docs, which describe such SolidJS intricacies? Because this going against the core "fine-grained reactivity" promise. It seems completely undocumented and LLMs couldn't solve this issue. I spent 2+ hours on this and would like to understand the general principle/quirks not to waste time in the future.Yeah, but I have an array with 200-500 elements, so even if DOM doesn't update, every single attribute of every single array-element re-running for change of any element is undesirable.Are you noticing performance issues?
Because this going against the core "fine-grained reactivity" promise.Yes, I understand it might feel a bit counterintuitive. but engineering is about making trade offs and effects have some overhead, so the effects from jsx are often grouped together as a performance optimization. I don't think the effect-grouping is mentioned in the docs. The docs are more focussed on the happy path and don't include all the implementation details.
Are you noticing performance issues?No, I didn't notice a thing even with all the re-runs, but then I have a powerful laptop. I was able to get-rid of
classList
re-runs, but reactivity is a core SolidJS concept, so it's important to understand its issues/pitfalls.
but engineering is about making trade offs and effects have some overhead, so the effects from jsx are often grouped together as a performance optimization.I see. In case this trade-off is intentional, I think it would be worth to document it. In general, a page with all the pitfalls/issues/workarounds/patterns/anti-patterns would be super helpful. Thanks for confirming
You are very welcome!
There is some talks behind the scenes to put this (and some other frequently asked implementation details) in the docs btw. Apparently it is a question that pops up more frequently
That's great!
I'd suggest besides documenting implementation details/intricacies, to also make a page with pitfalls: various things that make total sense, but are not obvious at the beginning.
For example, even the fact that when you access
values()[i]
on every list element, it causes every element to re-render on any other element's change. With hindsight, it's logical: since values()
changed, everything that depends on values()
will re-render. The fact that values()[i]
specifically doesn't change is not known by the framework ... and that is why createMemo is useful.
I fixed it in my code, but it was not obvious from the start.
(Yeah, I know there are also "stores", but I mean generally: in order to understand how to properly use a framework, it's important to understand "why" and for that it's helpful to understand bad/wrong approaches)