Reactivity not referentially transparent?
I don't understand why CounterList doesn't work here: https://playground.solidjs.com/anonymous/656ba247-562b-4c2b-b32a-bf734e09fd90. Clicking the others works fine but the middle row of buttons doesn't update. At least there is an ESLint warning, but it seems like adding a let shouldn't change anything. It seems to be something with how the compile-time reactivity works. The message mentions "tracked scopes" but I have no idea how they work and there is very little mention in the documentation. Isn't Index a tracked scope?
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
46 Replies
What's wrong with
For
(given that your item is an object, not a primitive value).
Well, I know that the number of items in the list is fixed or at least changes infrequently. Originally I was working with data like
[{value: 1, unit: 'hours'}, {value: 1, unit: 'minutes'},{value: 1, unit: 'seconds'},{value: 1, unit: 'milliseconds'}]
, and there appears to be a small speedup in fps from switching from For to Index. As I understand it, because For is not keyed, it redoes the whole element every update, whereas Index updates only the milliseconds.For is not keyed, it redoes the whole element every updateFor is keyed to the object reference. So if it is re-rendering every row, you are replacing the objects in the array. Now sometimes that is unavoidable when the data comes from an external API—using a store and
reconcile()
with { merge : true }
can mitigate that.oh, it uses object identity or something? I generate the whole array from a timestamp every frame.
IDK, I am looking at Svelte and you can just give a value as the key, it seems easier.
The Solid counter part is using a store with
reconcile()
and { key: 'id' }
option.it's similar, but it looks like in Svelte you can write an arbitrary expression for the key, it is not limited to an attribute
This isn't using crazy keys, but I think it shows how flexible Svelte is compared to Solid. AFAICT though it is the shortest way to emulate Solid's Index behavior.
for example instead of destructuring you can use
as arr (arr[0])}
in the each
.For me the fundamental difference is that
For
moves the entire item DOM fragment in accordance with the associated object reference while Index
will only swap text nodes.
Is this about wanting to use the (numeric) array index as a key? (The thing that React always tells you not to do).hmm, so they both use ===: https://github.com/solidjs/solid/blob/c9049dc8ddb39fc2163082c7bde3f507eb1ce831/packages/solid/src/reactive/array.ts. I guess the difference is really that For has a diff algorithm type thing, whereas Index does a simple index-by-index check. In that sense Index does use the numeric index as a key.
My guess is that code primarily governs the reactive behaviour. The other aspect is the relationship to the DOM via https://github.com/ryansolid/dom-expressions
GitHub
GitHub - ryansolid/dom-expressions: A Fine-Grained Runtime for Perf...
A Fine-Grained Runtime for Performant DOM Rendering - ryansolid/dom-expressions
there is https://github.com/ryansolid/dom-expressions/blob/main/packages/dom-expressions/src/reconcile.js which looks like basically the same algorithm as For.
Index
demo: The DOM elements never change because Index
just blasts new strings into the text nodes:
https://playground.solidjs.com/anonymous/0467cd68-f465-4ed0-8c48-1d5081d2dcbc
Plain For
with signal: item rows are always deleted and re-rendered.
https://playground.solidjs.com/anonymous/49128b6c-ebcf-4820-8d90-0dcbc9f6a8e3
For
with store and reconcile: existing item rows are are retained.https://playground.solidjs.com/anonymous/437026b8-e80a-4b7c-bcfc-74f986be43f1
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
we do have keyed-each counterpart just not built-in https://github.com/solidjs-community/solid-primitives/tree/main/packages/keyed#key
While you are here…
Why does this break?
if the breaking is the expression inside jsx not updating, it's because
count()
is outside of jsx. eslint plugin is correct here.So that item function isn't inside an effect and by extension each item JSX is a separate effect?
yeah, they're mini-components (untracked)
including For's
Good to know. Thank You!
in general, it's safe to assume a render function (children function) of a jsx component to be untracked
unless the author intentionally did it of course for some reason
could you make it be inside an effect? like wrap the (count) => { ... } function in a createEffect function?
you want createMemo or a fragment + iife here
i assume you're already aware that this would re-create the nodes
which solid tries to avoid most of the times and don't really need to if you're using nested signals (manually or with proxy through stores)
well in this case countList() is already a signal/memo. I guess it is not too important, it is mainly to write
c
instead of count()
so the JSX is not too longif you don't mind a little bit of overhead, you can use
mergeProps(...)
I guess I'm not clear, why is count a signal for Index but a value for For?
i'm not a native speaker so my language might be a bit simpler and easier to understand i guess 😆
Index cares about changes at specific indices whereas For cares about the individual items
but, is there a technical reason why Index could not pass values to its children function?
i'm not sure i understand the question
i guess this already contains the answer?
If I had to guess it's because the value is reactively projected into the rendered DOM. In the case of
For
that is not necessary because the item fragment lives and dies with the reference of the object that was used to create it.like with Key, everything is a signal. so you have to write
item()
but with for, it is a value, so you can destructure
so it is much more convenientthis explains well
Index doesn't rerun the function when a value changes, instead it gives you a signal that you use it in the jsx
when using solid-js, i guess you probably get use to it when it comes to destructuring
but is there a reason one couldn't make a version of Index that acted like For and reran the function?
like this but same api as Index?
If you rerun the function you are recreating DOM.
For
only runs the function for objects it hasn't seen before.well like Key for example, a version that didn't pass signals
As peerreynders says it would probably have to use some sort of caching to avoid rerunning the function too much
i think this will eventually get to dom diffing runtime-wise
if you're okay with added mental overhead added by compilers, this is already possible
https://github.com/orenelbaum/babel-plugin-solid-undestructure
https://github.com/lxsmnsyc/solid-labels
The syntax suggests to me that it creates a mapping from
item.id
to the rendered fragment, assuming that the content of the item
never changes.yeah, I think you would also add a parameter for comparing the
item
against its cached version. Or just render the fragment and compare the fragment with dom-diffingkeyed rendering assumes that the rendered
item
is immutable.well of course, everything is immutable. but if you change the items list then there is a new
item
and an old item
matched by the id / keyGiven
then
That is all it cares about.
Right, but say you have:
Then you can run the render function
{id, a, b} => [id, <div>{a}+{b}</div>
and get
And then turn that into a DOM text modification using a diff algorithm.
And with a Sufficiently Smart Compiler you can skip the diff and compile the render function directly into an incremental DOM update function
I was hoping the compilers you referred to were these magical incremental update compilers, but it seems they are just minor syntax hacks to make using signals easierSolid uses a compiler for JSX. SolidStart for 'use server' and file routes.
Svelte is the one with “magical” compilers (and I guess now React).
Simple is better than easy A lesson that comes hard for fine-grained reactivity. Explicit and consistent conventions even if they require more effort are worth it. The aim is to provide minimal tools to serve as the basis to build upon.https://www.solidjs.com/guides/getting-started#4-simple-is-better-than-easy
SolidJS
Solid is a purely reactive library. It was designed from the ground up with a reactive core. It's influenced by reactive principles developed by previous libraries.
Ryan Carniato
YouTube
JavaScript Frameworks - Heading into 2024
It's my first stream of the new year. Let's talk about JavaScript frameworks. Where things have been and where they are going.
[0:00} Starting Soon...
[2:30] Preamble
[8:45] The Plan for Today
[17:00] Ryan's Predictions Last Year
[27:00] React in 2023
[33:30] React - Server Components with No Server
[45:00] React - Next & Server Actions
[57:30]...
Ryan Carniato
YouTube
JavaScript Frameworks - Heading into 2024
It's my first stream of the new year. Let's talk about JavaScript frameworks. Where things have been and where they are going.
[0:00} Starting Soon...
[2:30] Preamble
[8:45] The Plan for Today
[17:00] Ryan's Predictions Last Year
[27:00] React in 2023
[33:30] React - Server Components with No Server
[45:00] React - Next & Server Actions
[57:30]...
lol alright, I was already thinking about Svelte, now even SolidJS devs are telling me Svelte is easier 😉
I have argued about simplicity with people before and I am firmly in the "you can't build an airplane without a million parts" camp
for example the way I started was combining SolidJS and Svelte components in a single app...
i saw https://fxtwitter.com/youyuxi/status/1800899548778479992 and had to think of this 🧵 . it's all about philosophies in the end. and in accepting that all abstractions leak in some way or another, u just gotta choose which type of leakage u prefer.
💬 2 🔁 1 ❤️ 69 👁️ 13.8K
FxTwitter / FixupX
Evan You (@youyuxi)
@Rafael_Casuso @emil_priver @sveltejs @Rich_Harris Also to many users the compiler-based approach is just too magical and works against standard JS intuition. It works well for Svelte’s audience but not necessarily for others. It’s another trade-off we are intentional about, not because we don’t understand it.