reactjs - Korrekt sätt att ta bort objekt från cache på klientsidan i apollo-client
Jag använder GraphQL med Apollo-Client i min React(Typescript)-applikation med ett minnesbaserat cache. Cachen uppdateras när nya objekt läggs till vilket fungerar bra utan några fel.
När objekt tas bort returneras en sträng från GraphQL Apollo-Server-backend som anger den lyckade raderingsåtgärden, vilket initierar uppdateringsfunktionen som anropas och som läser cachen och sedan modifierar den genom att filtrera bort objektets id. Detta utförs med hjälp av mutationshooken från 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) },
});
}
},
});
Funktionen fungerar och frontend uppdateras med de korrekta objekten i cache, men följande fel visas i konsolen:
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
Finns det ett bättre sätt att uppdatera cache så att detta fel inte tas emot?
Svar #1
Jag stötte också på exakt samma varning, och tyvärr kom jag inte på någon lösning annat än den som föreslås här: 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;
},
},
},
},
}
}),
});
(Jag är inte säker på om jag skrev dina fält och typer korrekt dock, så du kanske ändrar den här koden lite)
I grund och botten låter koden ovan Apollo-klienten veta hur man hanterar sammanslagningsbar data. I det här fallet ersätter jag helt enkelt de gamla data med nya.
Jag undrar dock om det finns en bättre lösning
Svar #2
Jag har också stött på samma problem. Jag har hittat en tråd på GitHub som erbjuder två alternativa lösningar här.
Den första är att rensa det som finns i din cache innan du anropar 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 rensar detta din cache så att dina nya data blir den nya sanningskällan. Det finns ingen oro för att förlora dina gamla data.
Ett alternativt förslag för apollo-client v3 finns längre ner i samma tråd:
cache.modify({
fields: {
notifications(list, { readField }) {
return list.filter((n) => readField('id', n) !==id)
},
},
})
Detta sätt tar bort mycket standardkod så att du inte behöver använda readQuery, evict och writeQuery. Problemet är att om du kör Typescript kommer du stöta på vissa implementeringsproblem. Under huven används formatet InMemoryCache istället för de vanliga GraphQL-datan. Du kommer att se Reference-objekt, typer som inte härleds och andra konstiga saker.
