-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cache data not evicted after returning INVALIDATE in modify #9319
Comments
@lprokein You can read more about the somewhat obscure role See especially this note:
If you don't mind removing/evicting the data, |
@benjamn thanks for your comments. I am still a bit confused what's the actuall purpose of INVALIDATE but at least I now now know it's not doing what I expected. I check the issue that you linked I kind of agree with this comment #7060 (comment) that I kind of miss a way how to invalidate specific queries after mutation the way that:
Please correct me if there is a way how to achieve this. Is there a way I can read whether the cached query is active/mounted or not so I can decide wheter to use DELETE or INVALIDATE ? I know client.refetchQueries has |
@benjamn I think the only reason I know maybe CRUD applications maybe aren't Apollo's main type of application, but Apollo still supports mutations, so having a way of dealing with stale data across queries in the application is important, and dealing with it in a visually appealing way is important too. There's no reason to evict data if that data is the best you have to show at the moment. I think this feature is so important that having to implement the Even more so if you consider what happens when inactive queries become active, if they contain data flagged invalid, those should be refetched too and calling |
I'm trying to make apollo client invalidate some paginated fields ( the GraphQL schema looks like something like the following: type List {
id: ID!
entityIds: [ID!]
x: XRelayConnection # { edges { node: X }, ... }
y: YRelayConnection # { edges { node: Y }, ... }
}
subscription listUpdate(list: ID!) {
list: List # { id entityIds } fetched
}
mutation addToLibrary(list: ID!, entity: ID!) {
list: List # { id entityIds } fetched
}
mutation removeFromLibrary(list: ID!, entity: ID!) {
list: List # { id entityIds } fetched
} The subscription is maintained during the app lifecycle to ensure the internal typePolicies.Library.fields.entityIds = {
merge(existing: string[] | undefined, incoming: string[], c) {
// Hack to access the parent `library` field, see side note
const libraryId: string | undefined = c.variables
? c.variables.libraryId || c.variables.id
: undefined
if (!libraryId) {
throw new Error('Must include one of `id`/`libraryId` as variable when fetching `MusicLibrary.ids`')
}
if (existing && incoming) {
const shouldInvalidate = { X: false, Y: false }
const remainingIds = new Map(existing.map((id) => [id, false]))
const invalidateTypeFor = (id: string): boolean => {
const type = parseId(id).__typename // we can infer typename from ID for this use case
if (type && type in shouldInvalidate && !shouldInvalidate[type]) {
shouldInvalidate[type] = true
}
return shouldInvalidate.X && shouldInvalidate.Y
}
// Invalidate due to new additions?
for (const id of incoming) {
if (remainingIds.has(id)) {
remainingIds.set(id, true)
} else if (invalidateTypeFor(id)) {
break // New ID encountered - invalidate corresponding type
}
}
// Invalidate due to removals?
for (const [id, wasKept] of remainingIds) {
if (!wasKept && invalidateTypeFor(id)) {
break
}
}
c.cache.modify({
id: c.cache.identify({ __typename: 'Library', id: libraryId }),
fields: {
x: (cached, ctx) => {
return shouldInvalidate.X ? ctx.INVALIDATE : cached
},
y: (cached, ctx) => {
return shouldInvalidate.Y ? ctx.INVALIDATE : cached
},
},
})
}
return incoming
},
} side note: The merge function has to be set on |
I have a query that returns list of projects created from a specific template. Query has a variable
templateId
- so for which template projects should be returned.I have a mutation for creating new projects.
After project is created though mutation I want to invalidate the cache for listing projects of the template which was used for creating this last project.
So I use cache.modify and return
INVALIDATE
for relevant item.But this does not invalidate the cache and there is no refetch next time calling query for listing projects of template.
I tried adding
broadcast: false
andcache.gc()
. No help.But when I return
DELETE
instead ofINVALIDATE
cache is deleted and refetch is called next time calling query for listing projects of template.So whats the point of INVALIDATE then? My understading was that INVALIDATE should be used for this use-case - based on reading docs and other issues. Am I wrong and should DELETE be used instead ?
Intended outcome:
When returning
INVALIDATE
, data should be invalidated and refetched next time.Actual outcome:
Data is stale and no refetch is initiated.
How to reproduce the issue:
Versions
The text was updated successfully, but these errors were encountered: