InMemoryCache: how to wait for a cache update?

After I create a new item, I manually add it to my local cache with a combination of readQuery and writeQuery. Afterwards I transition to a detail view of that item. My app ends up showing a 404 screen, because at the time I transition to the view, the cache hasn’t yet updated in any of my views.

I’ve tried simply awaiting the result of my writeQuery, but this doesn’t work. Is there another event/callback I can wait for to know my item’s available?

Thanks!

Hello! This is a tricky one to diagnose, but I find it surprising that cache hasn’t updated in time to show information on a detail page after writeQuery completes. If possible, could you share two snippets:

  • Your readQuery/writeQuery calls related to the item
  • Any queries executed by your detail page related to the missing item

Those will hopefully provide a little extra context to spot an issue.

Sure! I’m using Rescript, so the style will look a bit weird, but undearneath it’s just apollo-client@3.5.8

The result of writeQuery is a synchronous { __ref: "ROOT_QUERY" }, so it’s probably just ignorance on my part as how to wait for that operation to complete. I don’t see any callbacks I can wait for like queries and mutations.

After I call my onCompleted, my app pushes a new route to transition to the new element. The detail view 404’s for about 200 milliseconds before showing the item.

Here’s the code. I omitted some of lines of error handling for brevity:

let useReadTrip = () => {
  React.useCallback((cache, tripId) => {
    switch cache.readQuery(~query=GetTrip, {id: tripId}) {
    | Some(Belt.Result.Ok({trip})) => Belt.Result.Ok(trip)
    ...
    }
  })
}

let useWriteTrip = () => {
  React.useCallback((cache, trip) => {
    cache.writeQuery(~query=GetTrip, {id: trip.id}, ~data={trip})
  })
}

let useCreateTripElement = (~input, ~onCompleted, ~onError) => {
  let readTrip = useReadTrip()
  let writeTrip = useWriteTrip()
  let update = React.useCallback1((cache, result) => {
    switch (result.data, result.error) {
    | (Some({createElement: element}), None) =>
      readTrip(cache, input.tripId)
      ->Belt.Result.map(trip => {...trip, elements: [...trip.elements, element]})
      ->Belt.Result.map(trip =>
        writeTrip(cache, trip)->(
          result => {
            // TODO here: how can I know when writeTrip above is written to the cache?
            onCompleted(element)
          }
        )
      )
      ->ignore
    ...
    }
  }, [input])

  CreateElement.useWithVariables({input}, ~update, ~onError)
}

The readQuery, writeQuery, and a useQuery hook all use this query:

  query GetTrip($id: ID!) {
      trip(id: $id) {
        ...Trip
      }
    }

My detail view is just a few levels down in React, grabbing the element out of the useQuery’s trip.elements

Ooof, I finally figured this out. I had my global fetch policy set to NetworkOnly for some reason. This meant that when I manually modified the cache locally, this really just told Apollo that something was dirty and it should refetch. I’ve changed the policy to CacheFirst and it’s working as expected.