Fetching data in React is deceptively hard. You start off with a simple useEffect + useState combo and you think you're done.
"This is great!" yo...
For further actions, you may consider blocking this person and/or reporting abuse
With this setup, do you have a cache in both MobX as well as React Query? If yes, how do you determine which cache you should read from? Or does the React Query cache reference the MobX cache so whenever you update data in MobX, the change propagates to the React Query cache?
Also, how do you update data in your server data with a MobX action? In your linked code sandbox you use
.makeFavorite
on theBook
model to update the book in the MobX cache, but would you also need to use a react-query mutation to update your server data?I also would like to see how to make caching and mutations work. It seems pointless to me to use React Query with Mobx without these two features
Exactly. The data lives only in MobX. React Query caches only the references to the MST instances, so if you update the data anywhere, the change is visible everywhere.
You can use react-query mutations, and then update the MobX data once the request is successful.
Mutations are really practical as they handle the IDLE > LOADING > SUCCESS | ERROR state transitions and support retries and other react query goodies. However, you can just as simply run your async actions in useEffect.
great article man, appreciated.!
Thanks for the write up. Am wondering about the exact downside of disabling the
structuralSharing
param from React Query. For whoever might be interested in this, see [1].For info, the
queryCache
has been deprecated [2]. QueryClient should now be used [3].[1] react-query.tanstack.com/guides/im...
[2] react-query.tanstack.com/guides/mi...
[3] react-query.tanstack.com/guides/mi...
I've updated it!
What are the advantages of this over using a store that has normalization built in like resthooks.io/docs/getting-started/... ?
It seems like a lot of extra work vs
Nice article. I also missed automatic normalisation and data updates like in apollo client (but for anything including rest), so I recommend you to try
normy
- the library I recently created - github.com/klis87/normyIt already has
react-query
binding, so it basically allows you to use react-query just like you do without any extra code (apart from several lines of code to setup normalisation store) and you can enjoy automatic data updates.And bonus - it works with trpc too!
I love the idea of storing a reference to the mobx-state-tree instances inside the react-query cache because whenever you update your data from actions it's also updated in react-query cache automatically. But when we have this kind of code:
where we return an array of posts from fetch action, this array is going to be stored inside react-query cache and if we update it with setQueryData function like:
changes won't be tracked by state tree and we'll get unsynchronized data. But if we push it from actions it will be available in both (react-query cache and inside mobx-state-tree model).
To fix this we can return a "self" instance from fetch action so we can store the reference to the whole postsStore inside react-query cache and then any manual changes from outside (from react-query) should be performed by calling actions. But, I'm wondering if it's a correct solution at all to return a reference to the whole storage because it can possibly contain more fields that we don't really want to cache (even references to them).
So, my question is how did you solve this problem? Because in case we return an array it becomes very easy that someone (if you don't work alone) can change rq cached data like I did before with setQueryData and this array won't be observable anymore so any other changes to it from actions won't make any effect.
Nice article! If I understand correctly, using this method, each object only occurs once in the store, but you still fetch a whole page per query? The creator of react-query mentioned that with normalized caching you may end up replicating all the filtering, sorting, paging logic on the client side and you potentially still have stale data.
twitter.com/tannerlinsley/status/1...
In your opinion, does this still outweigh just refetching the data (and potentially overfetching) with react-query?
That's correct. I don't have to recreate the server-side logic because I use react-query to sync. The normalization makes sure that each object occurs only once in the store. This way, a resource on a list view and the same resource on the details view is always in sync. Also, updating the resource means that it will update both the list and details view.
a
This alone is already a great benefit with no downsides compared to plain react-query. But when you add to it all other benefits of MST...
Actions on models
you can do stuff like
article.like()
instead oflike(article)
Transparent denormalization
you can do stuff like
article.author.name
(Hereauthor
is a MST object with actions and views). Or evenarticle.author.update({name: "John Doe" })
. The API is as clean as it gets.Type checking
All of the properties, actions, views on the model are typechecked. When you type in
article.
you editor will list all the available props on the model, and it goes all the way down - type inarticle.author.
and you get all the props on the author model.As I see it - there no downsides when using RQ+MST combo - only benefits.
Interesting! I've thought a lot about this problem, and ended up writing a query library on top of mobx-state-tree: github.com/ConrabOpto/mst-query
Not as many features as react-query yet of course, but I personally think first class support for mobx-state-tree models is worth it.
Great article!
Are there any benefits react-query provides that we lose by storing our results in the state tree? In the tweet below, Tanner Linsley says he doesn't combine his global state tree (in his case zustand) until he needs them in the same place:
twitter.com/tannerlinsley/status/1...
You don't lose any benenfits. You have to write a bit more code for mst, but thats just the types that you have to write anyway if you use TS