Skip to content

setNormalizedData should keep old updatedAt for queries that are affected #36

@johanobergman

Description

@johanobergman

With react-query, let's say there's a books query with a staleTime of 10s:

await client.prefetchQuery({
  queryKey: ['books'],
  staleTime: 10_000,
  queryFn: () =>
    Promise.resolve([
      {
        id: '1',
        name: 'Book 1',
        comments: [...],
      },
      {
        id: '2',
        name: 'Book 2',
        comments: [...],
      },
    ]),
});

After 10 seconds have passed, the query is considered stale. Let's say it's no longer on screen, so it should be refetched at the next mount.

A call to setNormalizedData due to a mutation, or manually like this:

normalizer.setNormalizedData({
  id: '1',
  name: 'Book 1 updated',
});

will update the dataUpdatedAt property of every query that it touches. Book 1 in the books query will be updated and the books query will not be considered stale anymore and will not refetch at mount.


I think this behaviour is wrong, as even though we got some partial data back for Book 1, the rest of the data and the other books were not refreshed, so the books query should still be considered stale.

One use case I have is where I (as someone discussed in another issue) automatically call setNormalizedData when queries receive new data (like Apollo which I have used before does). But when I call queryClient.invalidateQueries() on a mutation (I still sometimes do even with Normy), all queries will first invalidate, but then setNormalizedData causes queries to immediately become valid again, if a previous query updates their data in some way.

Cause/fix

In the Normy internal function updateQueriesFromMutationData, react-query is called like this, which causes dataUpdatedAt to update:

queriesToUpdate.forEach(query => {
  queryClient.setQueryData(
    JSON.parse(query.queryKey) as QueryKey,
    () => query.data,
  )
})

The latest dataUpdatedAt timestamp can instead be preserved like this:

queriesToUpdate.forEach(query => {
  let queryKey = JSON.parse(query.queryKey) as QueryKey
  queryClient.setQueryData(queryKey, () => query.data, {
    updatedAt: queryClient.getQueryCache().find({ queryKey })?.state
      .dataUpdatedAt,
  })
})

Relevant discussion: TanStack/query#4716 (comment)

I made this as an issue instead of a PR since it could be seen as a breaking change (even though the current behaviour is not documented), so I wanted to get some input on this first. Another option could be to add a parameter to setNormalizedData where the user can decide if the dataUpdatedAt timestamp should be touched or not.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions