4

In my application, I have a paginated feed containing posts retrieved from the /feed endpoint.

Each feed post has...

  • postId
  • postTitle
  • postBody
  • postCreator (object)

Each postCreator object has...

  • userId
  • userName
  • userBio
  • userImageUrl

As suggested by Redux, I'm currently...

  • Extracting the postCreator objects and storing them in their own global usersStore (using Zustand)
  • Replacing the postCreator objects with the relevant postCreatorId (which references a user in the global usersStore) in each post
  • Storing the posts in their own postsStore (using Zustand)

The benefits of this are...

  • If a user updates their information (e.g. name or bio), we only have to update their record in the usersStore, and their information will be updated across the whole app (their post creator info in the feed posts UI, their profile image in the tab-bar etc)
  • We don't have to re-request the data from the /feed endpoint to get the posts containing the updated postCreator object. Especially considering that a user may have scrolled way down the feed at that point in time, so we may have to get hundreds of posts from the /feed endpoint, putting strain on our backend.

React-Query/SWR

I have recently discovered React Query and SWR and have heard a lot about how they "remove much of the need for global state", but I'm slightly confused by how they handle the above scenario. After doing some research on React-Query, I'm under the impression that there a few ways to handle this...

  1. Still use the global usersStore and postStore to update the user's information in a single place, as described above.

  2. Scrap the global usersStore, and update the postCreator information directly in React-Query's cache for the /feed infiniteQuery, using queryClient.setQueryData. But we would have to also update the other query caches where the user's information is returned, and what if the cache has expired?

  3. Scrap the global usersStore, and refetch all of the posts from the /feed endpoint when a user updates their info. However, if a user has scrolled way down the feed at that point in time, we may have to get hundreds of posts from the /feed endpoint, putting strain on our backend. React-Query infinite query refetch docs

What is the best practice way to handle this using React-Query/SWR?

Will Despard
  • 447
  • 1
  • 4
  • 17

1 Answers1

3

react-query doesn't have a normalized cache. That being said, mostly, there is no need to do any normalization - just keep what the api sends you as your state, retrieve it via useQuery, and get rid of all redux / zustand global stores that would otherwise hold your server state.

In your example, I see two queries:

  • useQuery(['user', id]) for the user data
  • useInfiniteQuery(['feed']) for the feed of posts

When rendering your feed, you'll likely render a Post component for each entry, which can in turn call the useQuery that retrieves the user data. If you are worried about the n+1 queries that are being made, and you are in fact already delivering the user-data with the feed, you can manually prime the user-cache in the onSuccess callback of the feed query - just take the user data and copy it to the user-cache.

That way, the user-cache becomes the single source of truth for user data, which also updates from the feed query.

TkDodo
  • 20,449
  • 3
  • 50
  • 65
  • Thanks! Yes, n+1 could be problematic. Re your second solution, you're suggesting we reference the user-cache for each postCreator when we render the feed? I guess the question is, as the user-cache is now no longer used to get async data from the backend (it's just used as a single source of truth to store user data), why is it preferable to just using a Redux/Zustand to store the user data? Unless you're also suggesting that when we get new user data (e.g when a user's profile is opened), we use useQuery(['user', id]) and update the existing user's record retrieved from the feed endpoint? – Will Despard Apr 05 '21 at 19:16
  • yes, when the profile page is opened, you can also just call useQuery(['user', id]) and profit from the fact that the cache is already pre-filled. This assumes that you have an endpoint that actually returns user data :) – TkDodo Apr 05 '21 at 20:13
  • Awesome! I'm going to give it a go and report back (and accept the answer if all is working as expected). Thanks a lot! – Will Despard Apr 05 '21 at 20:39
  • 1
    Just to confirm, I implemented this and it is absolutely the best solution! – Will Despard Oct 12 '21 at 13:25
  • So does that mean that if I have highly relational data RQ might not be for me? Seems like because RQ doesnt have a normalized cache, I could potentially double the in memory footprint of the cache by storing lots of duplicate data in that cache. – Timur Ridjanovic Jun 08 '22 at 09:49
  • Timur, I'm not sure I understand your issue, could you explain a little further? – Will Despard Oct 03 '22 at 23:54