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.
