I went with this solution but it’s not optimal/complicated,
(a bit of complexity business related for the root level to put aside, the root level being the same query without a folderId param)
Is there a better/easier way to do it?
Thank you,
import type { ApolloCache, NormalizedCacheObject } from '@apollo/client';
export interface RemoveItemCacheUpdateProps {
cache: ApolloCache<NormalizedCacheObject>;
items: { id: string; __typename: string }[];
deletedItems: string[];
sourceFolderId?: string;
destinationFolderId?: string;
}
const LIBRARY_ITEMS_FIELD = 'libraryItems';
export const removeItemsV3FromCache = ({
cache,
items,
deletedItems,
sourceFolderId,
destinationFolderId,
}: RemoveItemCacheUpdateProps): void => {
cache.modify({
id: cache.identify({ __typename: 'Query' }),
fields: {
libraryItems(existing, { storeFieldName, readField, INVALIDATE }) {
// root level
if (!sourceFolderId) {
if (!storeFieldName.includes(`"folderId":`)) {
return {
...existing,
items: existing.items.filter((ref: any) => {
const id = readField('id', ref);
return items.some((item: any) => item.id !== id);
}),
// TODO: update pageInfo?
};
}
}
// we remove the references to the deleted items from the cache
// evict is not sufficient since it can be populated again by other queries
if (storeFieldName.includes(`"folderId":"${sourceFolderId}"`)) {
return {
...existing,
items: existing.items.filter((ref: any) => {
const id = readField('id', ref);
return items.some((item: any) => item.id !== id);
}),
// TODO: update pageInfo?
};
}
// moved to destination folder, invalidate destination folder query
if (storeFieldName.includes(`"folderId":"${destinationFolderId}"`)) {
const args = JSON.parse(storeFieldName.replace(/^libraryItems:/, ''));
cache.evict({
id: 'ROOT_QUERY',
fieldName: LIBRARY_ITEMS_FIELD,
args: args,
});
cache.gc();
return INVALIDATE; // not enough, it does not trigger a re-fetch when necessary
}
// moved to root level, invalidate root queries
if (!destinationFolderId && !storeFieldName.includes(`"folderId":`)) {
const args = JSON.parse(storeFieldName.replace(/^libraryItems:/, ''));
cache.evict({
id: 'ROOT_QUERY',
fieldName: LIBRARY_ITEMS_FIELD,
args: args,
});
cache.gc();
return INVALIDATE; // not enough, it does not trigger a re-fetch when necessary
}
return existing;
},
},
});
// we evict I'm not sure why since it's not sufficient,
// https://www.apollographql.com/docs/react/caching/garbage-collection#dangling-references
// dangling references remains, and the canRead is only a UI filter
// it would always be true when the object is populate again in the cache
for (const item of items) {
const itemNormalizedId = cache.identify({ id: item.id, __typename: 'LibraryOriginalAudioItem' });
if (itemNormalizedId && deletedItems.includes(item.id)) {
cache.evict({ id: itemNormalizedId });
}
}
cache.gc();
};