zino-hofmann / graphql-flutter

A GraphQL client for Flutter, bringing all the features from a modern GraphQL client to one easy to use package.
https://zino-hofmann.github.io/graphql-flutter
MIT License
3.25k stars 620 forks source link

Mutation does not trigger update on watchQuery #1117

Closed Veeksi closed 2 years ago

Veeksi commented 2 years ago

Describe the issue So as the title says, when the mutation is done it doesn't trigger cache update that would trigger watchQuery to update the todo list.

To Reproduce To reproduce this issue, you can easily use this sample repository here: https://github.com/Veeksi/graphgl_issue

  1. Fetch all todos -> (Empty list at start)
  2. Create one todo by clicking "+1" symbol -> (Empty list still)
  3. Fetch all todos again by clicking refresh icon -> (Todo gets added)

Expected behavior

  1. Fetch all todos -> (Empty list at start)
  2. Create one todo by clicking "+1" symbol -> (New todo gets added)

Used version I used graphql_flutter version 5.1.0

budde377 commented 2 years ago

The problem is not that the cache isn't updated, but that the cache has no way of knowing how to link your newly created todo with the list of all todos. I.e.:

There is absolutely no way to reasonably implement this functionality from the example you've provided. Assume that we added every created to-do to all fields returning a list of to-dos, how would this look when you start filtering to-dos? E.g. listing to-dos for other users?

Now, how do you solve this without having to re-fetch the query from the API? You can modify the cache manually. Try and take a look at the writeQuery or writeFragment methods on the client: https://github.com/zino-hofmann/graphql-flutter/tree/main/packages/graphql#request-readquery-and-writequery.

Veeksi commented 2 years ago

So to update cache manually I had to use update callback like this:

@override
  Future<Todo> createTodo(String text, String userId) async {
    final result = await _client.mutate(
      MutationOptions(
        document: gql(GqlQuery.todoMutation),
        variables: {
          'text': text,
          'userId': userId,
        },
        fetchPolicy: FetchPolicy.cacheAndNetwork,
        optimisticResult: PartialDataCachePolicy.accept,
        update: (cache, result) {
          var queryRequest = Operation(
            document: gql(GqlQuery.todoQuery),
          ).asRequest();
          final data = _client.readQuery(queryRequest);
          cache.writeQuery(queryRequest, data: {
            "__typename": "Query",
            "todos": [
              // Add your new todo
              ...data?['todos'],
              result?.data?['createTodo'],
            ],
          });
        },
      ),
    );

which, adds new todo to list by using spread operator.