apollo-client icon indicating copy to clipboard operation
apollo-client copied to clipboard

watchQuery with "network-only" policy does not fire when optimistic data is written to cache

Open logivad opened this issue 1 year ago • 2 comments

Hello. I subscribe to watchQuery with "network-only" policy. Then I execute a mutation with optimisticData and make use of update function to fill the cache with optimistic data. On the second invocation of the update function I update the cache with server data. It works like a charm with fetch poilicy "cache-first" for example. But it does not work with "network-only" because in this case watchQuery fires only once - when server responses. I wonder if this is an expected behaviour. I thought watchQuery would fire twice - whenever cache is updated.

logivad avatar Jan 26 '24 06:01 logivad

Hi @vadim-loginov!

Generally, it is expected behaviour that with "network-only", you will not receive any updates from the cache before your initial network request has succeeded.

From your description, I'm not entirely sure if that's the case here, though - could you please clarify that?

Do you just not get these updates in the beginning, or for a longer period of time?

Also, how do you invoke the watchQuery and the mutation?

phryneas avatar Jan 26 '24 10:01 phryneas

Hi @phryneas! Thank you for your response. I use Angular and apollo-angular. Here is how watchQuery is used:

ngOnInit() {
    this.qRef = this.apollo.watchQuery({
      query: booksQuery,
      fetchPolicy: "network-only"
    });
    
    this.qRef.valueChanges.subscribe(({ data }) => {
      this.books = data.books || [];
      console.log("watchQuery fired");
    });
}

Here is the mutation:

addBook() {
  console.log("addBook called");
  const newBook = {
    // not important
  };
  this.apollo.mutate({
    mutation: gql`
      mutation AddBook($id: ID, $title: String, $author: String) {
        addBook(id: $id, title: $title, author: $author) {
          id
          __typename
          title
        }
      }
    `,
    variables: {
      id: newBook.id,
      title: newBook.title,
      author: newBook.author.name,
    },
    optimisticResponse: { addBook: newBook },
    update(cache, result) {
      console.log("update called")
      const book: Book | undefined = result.data?.addBook;

      if (!book) {
        return;
      }

      cache.modify({
        fields: {
          books(cachedBooks, { readField }) {
            if (cachedBooks.some((cachedBook: any) => readField('id', cachedBook) === book.id)) {
              return cachedBooks;
            }

            return cachedBooks.concat(book);
          }
        }
      })
    },
  }).subscribe(({ data }) => {
    console.log("mutation completed");
  });
}

So. Update function mutates cache twice per mutation invocation whereas watchQuery fires (I see "watchQuery fired" in console) only once per invocation. It goes like this:

addBook called
update called <- optimistic
[waiting for the server..]
update called <- real data
watchQuery fired
mutation completed
...
addBook called
update called <- optimistic
[waiting for the server..]
update called <- real data
watchQuery fired
mutation completed

etc.

But if I use "cache-and-network" it goes differently:

addBook called
update called <- optimistic
*********************
* watchQuery fired *
*********************
[waiting for the server..]
update called <- real data
watchQuery fired
mutation completed
...
addBook called
update called <- optimistic
*********************
* watchQuery fired *
*********************
[waiting for the server..]
update called <- real data
watchQuery fired
mutation completed

etc.

watchQuery is fired twice per mutation - every time the cache is modified in update function.

logivad avatar Jan 26 '24 13:01 logivad