virtua icon indicating copy to clipboard operation
virtua copied to clipboard

Improve SolidJS support

Open Azq2 opened this issue 10 months ago • 1 comments

Fix for #671, #609 and some parts from #666

  1. Replace <RangedFor> with native <For>
  2. Memorize item render result
  3. index is now signal instead of value
  4. cache support

Original problem:

Also, I see, current implementation for SolidJS is broken at all:

  1. It is necessary to use <For> instead of <RangedFor> (which is actually custom <Index>).
  2. The result of rendering children must be memorized.
  3. index must be a signal! The whole point of <For> is that it does not re-render the element when the position changes!

Otherwise, we are now seeing problems:

  1. Current problem from the issue.
  2. When changing the data list, a full re-rendering of all elements in the list occurs, we can forget about performance... :(

Azq2 avatar Apr 28 '25 15:04 Azq2

Also this PR have breaking changes: image

But I think it's justified.

Azq2 avatar Apr 28 '25 15:04 Azq2

Could you also introduce keepMounted, a list that accepts the indexes of the children it should keep mounted, even when offscreen?

This is a feature necessary to have sticky items, and it is already implemented in the react/vue variants.

ArjixWasTaken avatar May 02 '25 00:05 ArjixWasTaken

@ArjixWasTaken It doesn't look too hard. I'll try adding keepMounted and reverse when the current PR is merged.

Azq2 avatar May 02 '25 09:05 Azq2

imo it is in the scope of this PR, would you accept a PR to ur PR if I were to make one?

ArjixWasTaken avatar May 02 '25 09:05 ArjixWasTaken

A large PR is hard to review. It's better to split it into different PRs. I added cache to this PR only because it was reviewed earlier.

Azq2 avatar May 02 '25 13:05 Azq2

Released in 0.41.0. PR for keepMounted is also welcome!

inokawa avatar May 03 '25 12:05 inokawa

well this sucks, why must the index be decoupled from the data it's accompanied by?

index must be a signal! The whole point of is that it does not re-render the element when the position changes!

how does it achieve that?

ArjixWasTaken avatar May 03 '25 15:05 ArjixWasTaken

how does it achieve that?

@ArjixWasTaken This is standard SolidJS behavior: https://docs.solidjs.com/reference/components/for Here's an example: https://playground.solidjs.com/anonymous/f914193c-693f-43d4-9b5a-02aa6f864564

When the list changes, only index() gets updated — the previous components are not re-created.

I assume you're more of a React developer than a SolidJS one. This behavior is similar to how React works when using unique keys (which is, in fact, a requirement of this library). In SolidJS, there’s no such thing as key, each uniq item is determined by the reference itself.

To make this work correctly:

  1. You should use <For> instead of forEach / map (this done inside virtua).
  2. When updating data, the array reference must change, but the individual items' references should remain the same. That is, don't recreate the objects — mutate the array structure, not its elements.

well this sucks, why must the index be decoupled from the data it's accompanied by?

Now virtua is fully compatible with SolidJS. Why did that upset you? o_O


In general, when using signals or createStore, this isn’t usually an issue. However, some store libraries like solid-zustand or immer-based ones mutate both the array and its items when updating, which breaks this pattern. That approach is incompatible with how SolidJS tracks changes and won't work correctly (causes unnecessary renders). In such cases, it's common to separate the list (just an array of ids) from the actual data (stored in a Record<id, Item>).

However, this goes beyond the scope of this library.

Azq2 avatar May 03 '25 19:05 Azq2