nested mutability - setQueryData
I understand that the top level data must be updated in an immutable way (spreading object, array or otherwise returning a reference to a new object !== old reference.).
But, say I have an array of arrays - should there be any reason that once I have a new reference in hand (
.filtered from oldData with one top level array filtered out, say) - setQueryData should work if I then update one of the underlying arrays, right?
Meaning - does setQueryData need to be immutable all the way down, or just the top level (structuralSharing is off)? I assume its the latter, but I'm hitting strange behavior where I create a new reference, and then mutate a child, but the query data doesn't update.17 Replies
fuzzy-lavender•3y ago
immutable all the way down
fair-roseOP•3y ago
Can you please point me to documentation where this is stated? Or maybe code that enforces/depends on this? Also - would you accept a docs pr that specifically states this?
fuzzy-lavender•3y ago
Updates from Mutation Responses | TanStack Query Docs
When dealing with mutations that update objects on the server, it's common for the new object to be automatically returned in the response of the mutation. Instead of refetching any queries for that item and wasting a network call for data we already have, we can take advantage of the object returned by the mutation function and update the exis...
fuzzy-lavender•3y ago
I find it interesting that, when working with react, we would think that mutating something in place is a good idea / will work. It's not a good idea for the
useState setter either ...fair-roseOP•3y ago
I read this several times actually - the confusing thing with this api is that I'm used to immutable apis (not including specific functional libraries, which I don't use) to only necessitate
shallow immutability, i.e. top-level referential immutability. It isn't a crazy leap to enforce immutability all the way down but it isn't immediately intuitive to me and did actually cause confusion in my team. Maybe specifying the example to include a nested mutation is a good idea?
Also - out of curiosity of the library internals - why is immutability required all the way down?
Maybe my intuitive assumption about shallow immutability is completely wrong and understanding the internals will help me intuit better about apis like this in the futurefuzzy-lavender•3y ago
can you show an example where you get weird behaviour when you mutate something at a lower level?
fair-roseOP•3y ago
I think. Let me mangle it a bit.
Never mind the style etc., just wondering about correctness vis a vis the react-query cache
fuzzy-lavender•3y ago
so with structural sharing off, this should probably work
fair-roseOP•3y ago
I see that structuralSharing is on by default - which I didn't know
But that doesn't necessarily explain the behavior, right?
Even if old inner references are maintained, the top level reference is changed and should trigger a rerender anyway, no?
fuzzy-lavender•3y ago
you would get:
- a new reference for the top level object
- the same reference for the inner array
ah, wait a sec
structural sharing actually makes sure that you'll get the same top-level reference back if "nothing changed" inside of it. So if the array is the same instance, structural sharing will think that nothing changed, so even though you spread the top level array, it will give you back the old one
fair-roseOP•3y ago
Interesting. This is confusing to me that that is the default api - does that surprise you?
Its confusing to me because it has kind of a "deep equal"y behavior to some extent, which I wouldn't assume to be the default (unless explicitly stated, like with queryKeys)
fuzzy-lavender•3y ago
it's an optimization that works really well to avoid informing components about changes that they are not interested in, but it does expect you to work immutably. Immutable updates are all over react, like I said, with useState, you also have to work immutably, and with redux, too.
if you want to mutate things, I suggest using something like
immer
Its confusing to me because it has kind of a "deep equal"y behavior to some extent,yes, it is. if we didn't have it turned on, every background re-fetch would re-render all components, because json parsing a new network response is a new object every time .. but with this, we can check if something really changed, and opt out of updates most of the time that's why it's on per default tbh, most people don't write to the cache directly, they just invalidate() and get that out of the box. those that do write to the cache directly are encouraged to do it immutably. I don't understand why you'd think that top level immutability would be enough. redux selectors also would break in that way ...
fair-roseOP•3y ago
I'm not really a redux user.
What's the actual algorithm used for structuralSharing by default?
fair-roseOP•3y ago
React Query Render Optimizations
An advanced guide to minimize component re-renderings when using React Query
fuzzy-lavender•3y ago
GitHub
query/utils.ts at 765f6196e900c1dd826b040773fcf97839ca60d0 · TanSta...
🤖 Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query. - query/utils.ts at 765f6196e900c1dd826b...
fair-roseOP•3y ago
I see. Thank you very much - this is literally the code that causes the behavior.
Thank you for the time, attention and friendly responses. This is my favorite library!
I guess - rule of thumb: Immutability all the way down.
Other options:
* turn off structuralSharing if I really want to for this query (because this may be slow for a lot of data).
or:
* implement structuralSharing for this query to only check for top-level reference equality
fuzzy-lavender•3y ago
yep, those are the 3 options 👍