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

Refetch queries with mutations subscription #74

Closed
jgoux opened this issue Feb 28, 2019 · 6 comments
Closed

Refetch queries with mutations subscription #74

jgoux opened this issue Feb 28, 2019 · 6 comments
Labels
feature New feature or request Stale

Comments

@jgoux
Copy link

jgoux commented Feb 28, 2019

Hello,

Description

One important feature of a graphql-client to me is the ability to refetch/update the data of a query after one or multiple mutations executions.

I really liked the idea of "a query subscribes to one or more mutations" here (see onMutation parameter) : https://github.com/arackaf/micro-graphql-react#building-queries

I started playing with the idea that we could leverage the useEffect wrapping the query fetching call and pass extra inputs to its second argument to automatically refetch the query again.

The extra inputs would take the form of a list of mutations results so whenever a subcribed mutation happens, the extra inputs changed and the query is fired again.

Suggested implementation

useQuery(GET_TODO, { id }, { refetch: ['addTodo', 'updateTodo', 'deleteTodo'] })

I made a working example of the idea here : https://codesandbox.io/s/m3nq0315m9

The general idea is to maintain of global registry of mutations results in the Context.
From here, useQuery could accept a refetch options taking a list of mutations names. Then it's just a matter of mapping the list of mutations names into a list of mutations results, and voilà, we have our extra inputs to give to useEffect.

The approach is naive but quite effective, we could complexify it a bit by accepting a tuple with an extra function given the mutation result, so we have control over the generated input given to useEffect :

useQuery(GET_TODO, { id }, { refetch: [['removeTodo', ({ id: removedId }) => id === removedId]] })
@jgoux jgoux added the feature New feature or request label Feb 28, 2019
@Joezo
Copy link
Contributor

Joezo commented Feb 28, 2019

We've been thinking about different ways of solving this problem. I'm not against the way you've suggested although I think I would prefer telling the mutations which query they should update, rather than this way around.

There's a related issue here #52.

This is very much open for discussion at the moment.

@jgoux
Copy link
Author

jgoux commented Feb 28, 2019

@Joezo Could you elaborate about why you prefer to do the subscriptions on the mutation side?

As I see it, the mutation is a data producer, and the queries are data consumers. Putting these dependencies on the data producer feels weird as it will have to know about every consumers. On the contrary, making each consumer decide for itself when it has to be refetch seems more flexible.
Generally there are far more read operations than write in an application. I prefer editing a few queries if I add a mutation than the other way around.

@bmullan91
Copy link
Contributor

bmullan91 commented Mar 1, 2019

@jgoux This is a very interesting idea, I've always thought of the dependency as one mutation to many queries though.

I could see this working well for simple queries/mutation with no arguments - like the todo's example you gave. But what about mutations and queries with arguments how would to correctly verify that a query should be refetched?

Another concern I have is this puts a lot of the onus of cache invalidation on the library; a notoriously tricky task. For example in Apollo it will update the cache if it's a single entity, for anything other than a single entity update will require either a refetch or manually updating the cache.

source: https://www.apollographql.com/docs/react/essentials/mutations.html#update

Back to graphql-hooks: we already support the refetch function, and have a ticket to add updating the cache after a mutation #52. I'd love to see a PR for how this would be implemented so we could properly evaluate it.

@reverie
Copy link

reverie commented Mar 17, 2019

@bmullan91 Would you consider implementing a cache policy like urql's? That library invalidates every query that resulted in a given model whenever a mutation returns an instance of that model. It's a blunt tool but it's been good enough in my current project.

@bmullan91
Copy link
Contributor

@reverie I've considered it but as you mentioned it's a very heavy-handed approach that would invalidate many queries unnecessarily. I wish to keep the library cache invalidation agnostic - leaving that up to the developer to make the right decision.

The implementation I have in mind for #52 will include an event-emitter, meaning the developer could implement that behaviour themselves; perhaps we could write up a guide for this in the docs.

@jgoux
Copy link
Author

jgoux commented Mar 18, 2019

@bmullan91 Using an event-emitter makes sense! 💡

I adapted my codesandbox implementation using mitt, the code is now very simple. 😄

To answer your question :

But what about mutations and queries with arguments how would to correctly verify that a query should be refetched?

To refetch a query, you can subscribe to a list of mutations. Either you provide the mutation name which always refetch the query when the mutation succeeds or you provide a tuple [mutationName, shouldRefetch] which allows to refetch the query conditionally based on the mutation's result :

useQuery(GET_TODO, { id }, { refetch: [['editTodo', ({ id: todoEdited }) => id === todoEdited]] })

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

No branches or pull requests

4 participants