reactjs - Korrekt måte å fjerne element fra klient-side cache i apollo-client


Jeg bruker GraphQL med Apollo-Client i min React(Typescript)-applikasjon med en minnehåndtert cache. Cachen oppdateres når nye elementer legges til, noe som fungerer bra uten feil.

Når elementer fjernes, returneres en streng fra GraphQL Apollo-Server-backend som angir den vellykkede sletteoperasjonen, noe som initierer at oppdateringsfunksjonen kalles, som leser cachen og deretter endrer den ved å filtrere ut ID-en til elementet. Dette utføres ved å bruke mutasjonskroken fra Apollo-Client.

const [deleteBook] = useMutation<{ deleteBook: string }, DeleteBookProps>(DELETE_BOOK_MUTATION, {
    variables: { id },
    onError(error) {
      console.log(error);
    },
    update(proxy) {
      const bookCache = proxy.readQuery<{ getBooks: IBook[] }>({ query: GET_BOOKS_QUERY });
      if (bookCache) {
        proxy.writeQuery<IGetBooks>({
          query: GET_BOOKS_QUERY,
          data: { getBooks: bookCache.getBooks.filter((b) => b._id !== id) },
        });
      }
    },
  });

Funksjonen fungerer, og frontend er oppdatert med de riktige elementene i cachen, men følgende feil vises i konsollen:

Cache data may be lost when replacing the getBooks field of a Query object.

To address this problem (which is not a bug in Apollo Client), define a custom merge function for the Query.getBooks field, so InMemoryCache can safely merge these objects:

  existing: [{"__ref":"Book:5f21280332de1d304485ae80"},{"__ref":"Book:5f212a1332de1d304485ae81"},{"__ref":"Book:5f212a6732de1d304485ae82"},{"__ref":"Book:5f212a9232de1d304485ae83"},{"__ref":"Book:5f21364832de1d304485ae84"},{"__ref":"Book:5f214e1932de1d304485ae85"},{"__ref":"Book:5f21595a32de1d304485ae88"},{"__ref":"Book:5f2166601f6a633ae482bae4"}]
  incoming: [{"__ref":"Book:5f212a1332de1d304485ae81"},{"__ref":"Book:5f212a6732de1d304485ae82"},{"__ref":"Book:5f212a9232de1d304485ae83"},{"__ref":"Book:5f21364832de1d304485ae84"},{"__ref":"Book:5f214e1932de1d304485ae85"},{"__ref":"Book:5f21595a32de1d304485ae88"},{"__ref":"Book:5f2166601f6a633ae482bae4"}]

For more information about these options, please refer to the documentation:

  * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers
  * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects

Finnes det en bedre måte å oppdatere hurtigbufferen på slik at denne feilen ikke oppstår?

Svar #1

Jeg møtte også akkurat den samme advarselen, og kom dessverre ikke opp med en løsning annet enn den som ble foreslått her: https://go.apollo.dev/c/merging-non-normalized-objects

const client = new ApolloClient({
  ....
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          getBooks: {
            merge(existing, incoming) {
              return incoming;
            },
          },
        },
      },
    }
  }),
});

(Jeg er ikke sikker på om jeg skrev feltene og typene dine riktig, så du kan måtte endre denne koden litt)

Grunnleggende lar koden ovenfor Apollo-klienten vite hvordan den skal håndtere sammenslåbar data. I dette tilfellet erstatter jeg rett og slett de gamle dataene med nye.

Jeg lurer imidlertid på om det finnes en bedre løsning

Svar #2

Jeg har også møtt det samme problemet. Jeg har kommet over en GitHub-tråd som tilbyr to alternative løsninger her.

Den første er å tømme det som er i hurtigbufferen din før du kaller cache.writeQuery:

  cache.evict({
    // Often cache.evict will take an options.id property, but that's not necessary
    // when evicting from the ROOT_QUERY object, as we're doing here.
    fieldName: "notifications",
    // No need to trigger a broadcast here, since writeQuery will take care of that.
    broadcast: false,
  });

Kort sagt tømmes hurtigbufferet ditt, slik at dine nye data vil være den nye sannhetskilden. Det er ingen bekymring for å miste dine gamle data.

Et alternativt forslag for apollo-client v3 er lagt ut lenger ned i samme tråd:

cache.modify({
  fields: {
    notifications(list, { readField }) {
      return list.filter((n) => readField('id', n) !==id)
    },
  },
})

Denne måten fjerner mye standardkode, så du trenger ikke å bruke readQuery, evict og writeQuery. Problemet er at hvis du kjører Typescript, vil du støte på noen implementeringsproblemer. Under panseret brukes formatet InMemoryCache i stedet for de vanlige GraphQL-dataene. Du vil se Reference-objekter, typer som ikke blir utledet, og andre rare ting.

Relaterte artikler