Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalidate by ID in react-query #29

Open
CptJJ opened this issue Oct 2, 2024 · 10 comments
Open

Invalidate by ID in react-query #29

CptJJ opened this issue Oct 2, 2024 · 10 comments
Labels
enhancement New feature or request

Comments

@CptJJ
Copy link
Contributor

CptJJ commented Oct 2, 2024

It would be awesome if there was a way to invalidate and/or refetch all queries based on an ID. I often find myself 3-4 layers deep in a nested object and I need to invalidate every single list view in all of its parents, but I don't want to invalidate pages of those lists if they don't contain the child. This would be especially useful paired with paginated/infinite query helpers and is also a great onError default for failed mutations.

I would love to help implement this, @klis87 could you point me in right direction if this is possible and what methods from core need to be wrapped.

@klis87
Copy link
Owner

klis87 commented Oct 2, 2024

Before I answer, why do you need invalidation instead of in place data update? Do you need adding items to/removing from arrays perhaps?

@klis87
Copy link
Owner

klis87 commented Oct 2, 2024

Anyway, this might be related to #22

in order to do this, we would need to expose https://github.com/klis87/normy/blob/master/packages/normy/src/create-normalizer.ts#L167C26-L167C55 as a separate method, so only to get list of dependent queries, without manual denormalization

Anyway, it is necessary to do this only for operation on arrays, like pushing, removing, sorting. If you want to invalidate only to update properties of existing objects, you are covered automatically, no matter where those objects are and how deep. In the future I hope to also cover arrays wit #1

@CptJJ
Copy link
Contributor Author

CptJJ commented Oct 2, 2024

Before I answer, why do you need invalidation instead of in place data update? Do you need adding items to/removing from arrays perhaps?

Four main use cases for me:

1: adding and removing to lists, many layers deep. I typically can get the IDs of all the parents very trivially but would also be great if these were tracked by Normy. Add/remove list helpers could be great here also!

2: Failed mutations. I often find it easy to optimistically update mutations and refetch on failure. Saves some complexity of saving the old state to revert back to.

  1. Calculated data. Think "cart total" that is driven by data of the "items" array in the cart, and there is a list view of many carts. While you can modify the cache directly, it's super convenient to just invalidate that list view,

4: peer to peer realtime updates. To save having to build a fully authenticated realtime server, it easy to just broadcast what each client changed to all other clients listening in a channel. Then the clients could just invalidate all "ids" they receive.

@klis87
Copy link
Owner

klis87 commented Oct 2, 2024

  1. so yeah, in my previous message, it should work like this, normy, based on any data, can find all dependent queries, then, everything that would need to be done is refetching those found keys
  2. Add automatic roolbackData #11 this might be interesting to you, this is actually something I would like to do in the near future. So basically you will get automatic revert, data to revert will be computed automatically
  3. 👍
  4. I am not sure, but this could be also handy for you - https://github.com/klis87/normy/tree/master/packages/normy-react-query#useQueryNormalizer-and-manual-updates-arrow_up - you get updated data inside a notification, you pass it to setNormalizedData and all dependent queries will be updated in place. Alternatively, by doing the feature with automatic invalidation we are discussing, we would refetch all dependent queries instead of in-memory updates (from example I pasted)

@klis87 klis87 added the enhancement New feature or request label Oct 2, 2024
@CptJJ
Copy link
Contributor Author

CptJJ commented Oct 2, 2024

Re 4, I'm using react-query specifically to avoid having to build some kind of sync engine/ proper realtime service, so data over the wire is exactly what I'm trying to avoid, however it might not be as bad as I was thinking.

I was imagining a lightweight server, possibly with no authorization, that lets any client subscribe to any channel, but only receives IDs that it then needs to decide itself what to refetch. Latency is no issue here just keeping things relatively in sync within a few seconds. I currently handle this via polling.

This could definitely be a horrible approach 😂

@klis87
Copy link
Owner

klis87 commented Oct 2, 2024

Re 4, just remember, that over the wire you would need to only pass objects with ids and attrs only which were actually changed, like imagine an action like updated articles with id 1 and 2, all you need to pass is:

[{ id: 1, name: 'Updated name 1' }, { id: 2, name: 'Updated name 2' }]

then on push notification, all your queries will be updated immediately. On the contrary, if your strategy is to refetch everything, you could end up with refetching dozens of requests simultaneously, which could cause perf issues. Anyway, I would really like to have this feature built-in, as it is important to give a choice, sometimes invalidation might be just better

@CptJJ
Copy link
Contributor Author

CptJJ commented Oct 3, 2024

Agree that refetching is dangerous... invalidating is the correct thing to do here as then only active queries are refetched and other views are refetched in the background when navigating to pages that use them.

@CptJJ
Copy link
Contributor Author

CptJJ commented Oct 4, 2024

Ran across another good use case:

When doing optimistic updates you going to want to cancel all in-flight queries to avoid having your update rolled back before the mutation completes. You can probably cover 99% of cases via something along the lies of:

queryClient.cancelQueries({
  queryKey: getQueryKeysById(
    event.mutation.state.variables.id,
  ),
});

@klis87
Copy link
Owner

klis87 commented Oct 4, 2024

This is actually very interesting case, I never thought about that one.

@CptJJ
Copy link
Contributor Author

CptJJ commented Oct 4, 2024

Here is a more complete example:

If you dont mind the refetching overhead, and dont want to build out api endpoints that return mutated data, this gives you a really awesome UX as long as your passing in full normalizable objects to your mutation variables

CleanShot 2024-10-04 at 19 03 52@2x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants
@CptJJ @klis87 and others