structural sharing do not preserve granularity
using a mutation to update a value in an object force the query to update the full object and not just the singular property.
I was wondering if this is a bug or there is a way to preserve the granular updates
I made a reproducible stackblitz:
https://stackblitz.com/edit/solidjs-templates-xkf75n?file=src/App.tsx
36 Replies
harsh-harlequin•3y ago
what do you mean with "granularity" ? Can you describe what you'd expect to happen in that example vs. what you are seeing?
stormy-goldOP•3y ago
basically as you can see in the example I linked, I have an object with multiple properties, when I call the mutation I just change one tho (
luckyNumber
), despite that, the createEffect with other properties (such as Name
, surname
...) get triggered, in solidJs it should get triggered only the one you change, so in this case luckyNumber
I don't know if you got the pointharsh-harlequin•3y ago
so in this case,
query.data
has to be a new object, because something inside it changed. name
, surname
, age
etc. are all primitives, so they will be equal anyways. Not sure why solid would trigger an effect if you depend on query.data?.name
?
In React, this wouldn't re-run unless name really changed:
where structural sharing comes in handy is if you have nested objects, like:
now even though data.luckyNumber
is changing, and data
is a new reference, data.names
will be the same reference.stormy-goldOP•3y ago
yeah but for some reason it get retriggered, not sure why
harsh-harlequin•3y ago
yeah I don't think there's much we can do on the structural sharing side of things
rival-black•3y ago
@ryansolid @Alex Lohr any ideas on what we could do?
exotic-emerald•3y ago
Hi, there!
I'm not sure, do you already use the storage option in createResource internally?
Ah, you're not using createResource only in the BaseQuery, without options.
harsh-harlequin•3y ago
structual sharing is a core feature, so it does the same thing in react and in solid
exotic-emerald•3y ago
Can you show me the part of the code that handles this?
Ah, found it already.
rival-black•3y ago
Right now we are solving this by wrapping the data result in a store and running the create effect on it which updates the store every time, but we have to do this all over the place and it’s not ideal
I would have hoped it was possible to abstract it directly within solid query
exotic-emerald•3y ago
It would really help if you could expose the createResource options so you could make use of the storage option.
Should I do a PR?
rival-black•3y ago
How would that work in user land code? How would it look like?
exotic-emerald•3y ago
rival-black•3y ago
But I don’t understand, wouldn’t that be equivalent to structuralSharing with the reconciler, like in our example?
exotic-emerald•3y ago
Unfortunately, the result is still fed to a signal, which then updates all effects subscribed to it.
Since resources don't have an equality comparison, it is run in any case.
rival-black•3y ago
Oh I see
exotic-emerald•3y ago
@TkDodo 🔮, I'm happy to create a PR to tanstack/solid-query to expose the resource options later in the evening, if you want it.
harsh-harlequin•3y ago
gonna ping @Aryan , he's the expert 🙂
vicious-gold•3y ago
Or rather only a top level equality check so every change would get past the equality check
I think given how all in Tanstack Query is I'd just use the Store internally and maybe not expose this
createResource is purposely lower level
exotic-emerald•3y ago
But you only need the storage option if structural shaping is enabled.
fair-rose•3y ago
Thank you Alex! Yeah this is a little tricky. We have this issue fixed in v5! The same example works as expected here https://stackblitz.com/edit/solidjs-templates-vacd3v?file=src%2FApp.tsx. I'll try to update v4 to work in a similar fashion so that it doesn't cause undesired behavior.
fair-rose•3y ago
I have my hands full at work this Friday. But I'll try to fix it over the weekend. Also if anyone else wants to take a stab at it. Please feel free to open a PR in the meantime 😄
The fixed version is on the
alpha
branch for reference
This approach would be a little more difficult to implement
The QueryClient is the main framework agnostic part of TanStack Query. This same client is used between multiple frameworks like vue
react
svelte
and solid
. So Adding a resourceOptions
to QueryClient would not be ideal since it won't make sense for other frameworks. What we could do is wrap the QueryClient exported by solid query to take in resourceOptions
but that would require a lot more work.
Alternatively what I was thinking, I dont know how familiar you are with the Tanstack query codebase Ryan 😅 But would it make more sense to use the from
primitive (https://www.solidjs.com/docs/latest/api#from) in solid-js instead of a store here https://github.com/TanStack/query/blob/alpha/packages/solid-query/src/createBaseQuery.ts#L51-L53. This would allow us to hand the structuralSharing capabilities back to TanStack Query and we wont need to use the reconcile
hack anymore. The referential equality will be maintained by Tanstack query and nothing would need to be done in solid-js land anymore. I'll try to work on an example to make this point clearervicious-gold•3y ago
I'm not sure that will work. We don't diff in our framework so structural sharing doesn't really matter
Like the problem is that the root signal changes which means everything else will change.
exotic-emerald•3y ago
If I understand correctly, query-solid can access all the options, thus checking if structuralSharing is intended should be trivial. So instead, we can simply prime the resource with the correct options depending on the use case.
I'm not too deep into your codebase (yet), but I maintain the resource-related packages in solid-primitives, so I feel mostly at home.
harsh-harlequin•3y ago
if solid doesn't need structuralSharing, I suggest you remove that option from
createQuery
and pass it as false
to the QueryObserver. It does have a runtime overhead.exotic-emerald•3y ago
There's not a 100% overlap of structuralSharing and using a deep signal as storage for the resource, since solid's reactivitiy is based on signals rather than immutability.
We can also make deep signal a default and remove the structuralSharing. It should have a minimal performance impact for those cases that don't need it.
There's an issue with using a store resource; you can no longer rely on using the resource directly to trigger effects. I may have a solution, though, but I need to test it thoroughly.
Unfortunately, an update signal breaks all kinds of other reactive assumptions.
So this is definitely something we want only to trigger in certain cases.
exotic-emerald•3y ago
Have a look if you're interested: https://github.com/atk/query/tree/atk-fine-grained-reactive-resource
GitHub
GitHub - atk/query at atk-fine-grained-reactive-resource
🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query. - GitHub - atk/query at atk-fine-grained...
exotic-emerald•3y ago
Infinite queries, relying on tracking the resource base, will fail (and a few others that do the same, too).
There are two options to go from here: fix all the other places not to rely on the resource base being reactive (e.g. using the
@solid-primitives/deep
package) or try to fix the structuralSharing in a way that uses a store instead of a signal to allow for fine-grained reactivity from solid.fair-rose•3y ago
After your feedback and @Alex Lohr helpful comments I think I have fine grained Query Data updates working! I have an example created on my dev package and here is the stackblitz showcasing it.
https://stackblitz.com/edit/github-ubelji?file=src%2FApp.tsx
I have exposed a
reconcile
option on createQuery
and QueryClient
options. The reconcile
option can take the following signature
fair-rose•3y ago
The
reconcile
option defaults to the id
key, to reconcile data updates similar to solid's defaults. It can also take any other reconciliation key as a string. Or if the user wants more control they can provide their own callback and do the reconciliation themselves 😄 with helpers like produce
. Or it can be set to false to avoid any reconciliationfair-rose•3y ago
fair-rose•3y ago
Surprisingly! The
reconcile
fn is way more performant than structuralSharing
function 🤯 I was able to load 250 distinct query observers without seeing any performance hits!vicious-gold•3y ago
It isn't surprising. This is why we wanted to move to reactive core. Unfortunately still need to support React. And getting back to structural sharing from here is even more expensive.
fair-rose•3y ago
That's amazing! I was struggling to mount 100 different queries in one component before. Now it just takes it like a champ hahaha. Thank you so much for jumping on this and helping out
fair-rose•3y ago
https://github.com/TanStack/query/pull/5287 Here is the PR/RFC of the proposed changes that brings fine grained updates to query updates! Any feedback, comment, concern is greatly appreciated 🙏
GitHub
feat(solid-query): Add
reconcile
option by ardeora · Pull Request...This pull request proposes a new feature for solid-query that allows for more precise updates to query data. The new reconcile option enables updates to be made between state updates, while the str...
rival-black•3y ago
I’ll test it as soon as I have some time available, thank you so much everyone for the time you took to get this PR ready so fast!