问题
The Apollo documentation discusses the use of cacheRedirects to tell Apollo how to access data that's already in the cache from other query.
It gives an example of this:
In some cases, a query requests data that already exists in the client store under a different key. A very common example of this is when your UI has a list view and a detail view that both use the same data. The list view might run the following query:
query ListView { books { id title abstract } }
When a specific book is selected, the detail view displays an individual item using this query:
query DetailView { book(id: $id) { id title abstract } }
We know that the data is most likely already in the client cache, but because it’s requested with a different query, Apollo Client doesn’t know that. In order to tell Apollo Client where to look for the data, we can define custom resolvers
I'm trying to understand why this is necessary for this example. If the books
query returns an array of type Book
, and the book
request returns a single object of type Book
, then surely the normalised cache will already have data for each of the books (from the ListView query) based on the typename and id, and the DetailView
query can use that information directly without any further intervention. Instead, we're told for write some code to help it:
const cache = new InMemoryCache({
cacheRedirects: {
Query: {
book: (_, args, { getCacheKey }) =>
getCacheKey({ __typename: 'Book', id: args.id })
},
},
});
Underwhat exactly which circumstance is the ApolloClient not able to figure this out for itself, and why?
回答1:
It seems obvious and intuitive that given a query like the one below that we're fetching a single Book
object by it's id
property.
query DetailView($id: ID) {
book(id: $id) {
id
title
abstract
}
}
However, in this example the name of the argument here (id
) just happens to match the name of the property used by the cache (id
). Outside convention, there's nothing that says the argument itself couldn't be called bookId
, bookID
or supercalifragilisticexpialidocious
. Even if a query returns a Book
type and takes one or more arguments, Apollo has no way to infer which argument is in fact the id that was used in cache normalization. Similarly, if other arguments exist, they may or may not matter in respect to whether what's currently cached can be used or not -- further logic is needed to determine that.
The other consideration here is that outside of optionally passing in an instance of IntrospectionFragmentMatcher
to your InMemoryCache
, Apollo is not actually aware of what the schema is of the endpoint it queries. The types used by the cache in normalization are determined after a query is fetched using the __typename
property. The whole point of cacheRedirects
is to prevent a query from being fired if the item or items are already in the cache. However, given a particular query, Apollo has no way to know that it will return a particular type until after that query returns. cacheRedirects
provides a way to say "this query will return this particular type" without ever having fired the query in the first place.
回答2:
Just an afterthought: this is inherently due to the GraphQL design. In GraphQL, book(id)
its resolver is free to interpret id
any way they like. It is simply a function call with a single parameter, which happens to return an instance of type Book
.
GraphQL actually says nothing about IDs whatsoever. It only recognizes __typename
as special.
Only Relay, and to some extent Apollo, add a notion of object IDs later. They take a bit different approach though, with Relay mandating your GraphQL schema into a more rigid, formal structure.
There is even additional complication (mentioned by the other answer too) about schema not being available to the client (so the client does not even know book
will return Book
without making the query). This again follows from GraphQL spec.
来源:https://stackoverflow.com/questions/52927310/when-to-use-apollos-cacheredirects