Open simeonkerkola opened 3 years ago
@sssmi I appreciate your clever use of a dynamic function for keyArgs
, but I suspect you may get further by disabling keyArgs
with keyArgs: false
, and relying instead on a read
function (along with your merge
function) to interpret the args
dynamically.
Here's some intermediate/advanced documentation about how I/we think about keyArgs
: https://www.apollographql.com/docs/react/pagination/key-args/
More specifically, I would guess your input
argument is the only one that really belongs in the keyArgs
array, because it's convenient to keep data for different input
strings completely separate. All the other arguments can be processed by read
and merge
—for example, your merge
function can return/store data in whatever order is most convenient internally, and later the read
function can dynamically sort that data according to the given ascendingSort
and sortField
arguments, providing different views of the same underlying data, without making network requests.
By the way : I feel like when an alias is setup on a query, apollo should use it instead of the original query name.
@benjamin, Working on a simulair thing.
We have an interface with sliders (like netflix) and we have the merge part workign using offset pagination in combination with some keyargs ( which are very unintuetive to say the least)
We all our items live in an item table in the DB, they are differentieted by item_type, show_data, Date_added. and we query with offset and limit.
Like described, we have pagination working as in a slider fetches more data when it hits the end. However, if i navigate from tha page and navigate back with cache still there.... it displays all items in one go, as in all of the data that has been fetched before.
Shoudl we use keyargs, or should we build some sort of custom read function.
@onair-lena you might want to track this thread ;)
We have a similar problem. Depending on the viewport, we are using an infinite scroll (with fetchMore
) or pages, using the exact same query.
Although not the best solution, @sssmi hack is working for us. We've used a silly directive (@include(if: true)
) instead of an alias, to keep the same data shape for both cases.
We've also tried defining a read
function as suggested. But, how can we know if we are in the paginated or infinite scroll case?
typePolicies: {
Query: {
fields: {
search: {
keyArgs: ['query'],
merge(existing, incoming, { args: { offset }}) {
// similar to https://github.com/apollographql/apollo-client/blob/5d0c881a4c20c5c48842d9a8d246d26a917eccf9/src/utilities/policies/pagination.ts#L33
},
read(existing, { args: { offset, limit }}) {
// how do we know here if we need to return the whole list or slice it?
},
},
},
},
};
@connection
directive. We've defined these two queries:
query searchPaginated($query: String!, $offset: Int){
search(
query: $query
offset: $offset
limit: 10
) @connection(key: "searchPaginated", filter: ["query", "offset"]) {
...Result
}
}
query searchInfinite($query: String!, $offset: Int) { search( query: $query offset: $offset limit: 10 ) @connection(key: "searchInfinite", filter: ["query"]) { ...Result } }
and in `typePolicies` only the `merge` function, so the `fetchMore` is working properly:
```ts
typePolicies: {
Query: {
fields: {
search: {
merge(existing, incoming, { args: { offset }}) {
// similar to https://github.com/apollographql/apollo-client/blob/5d0c881a4c20c5c48842d9a8d246d26a917eccf9/src/utilities/policies/pagination.ts#L33
},
},
},
},
};
I honestly think returning everything from the read
function (ignoring offset
and limit
) is the most flexible option for both infinite scroll and paginated navigation, because then you can use application/UI-level logic to slice the big array into chunks for pagination (in a part of your code where you probably have enough context to make that choice), and you don't have to keep updating the offset
and limit
of the original query each time you fetch new data, and both queries can consume the same underlying list of data (rather than maintaining separate lists in the cache).
This difference between paginated and non-paginated read
functions is discussed here in the docs. I'm specifically recommending the non-paginated approach.
If you're not sold on the one-big-list philosophy, and you want to keep using @connection
, keep reading for some additional thoughts…
Since keyArgs
is intended to replace @connection
, I would hope you can just use keyArgs
, but I admit @connection
can be useful to inject arbitrary key
information, and is conveniently already understood by most GraphQL tooling (unlike other directives or arguments you might make up).
While InMemoryCache
does support @connection
in the absence of keyArgs
, prior to AC3.5, there was a pitfall where providing keyArgs
would cause @connection
to be ignored (see #8659). Thanks to #8678 (released in v3.5), it's now possible to include information from any directives (or variables) in your keyArgs
array:
new InMemoryCache({
typePolicies: {
Query: {
fields: {
search: {
keyArgs: ["query", "@connection", ["key"]],
merge(...) {...},
},
},
},
},
})
This configuration will produce field keys like search:{"query":"...","@connection":{"key":"searchInfinite"}}
, so the @connection(key)
information is handled sort of like an argument passed to the field. You should not need to pass a filter
argument to @connection
, since that's what the other strings in keyArgs
represent.
i tried to use @connection directive as below and it returns me an error saying "Variable "$key" is never used in operation" . Can someone help with this?
query searchPaginated($query: String!, $offset: Int, $key: String = "key"){ search( query: $query offset: $offset limit: 10 ) @connection(key:$key) { ...Result } }
Any resolution to this? Why can't Apollo be updated to give access to the name of the custom query, seems like pretty important functionality for uses cases like what @simeonkerkola is doing. If an Apollo user ever wants to store data in the cache differently for any two custom queries then it more or less seems like that behavior is not supported by Apollo, because the only workable solution is hacking something together with @connection. This is basic functionality.
I recently updated the apollo client package in my work to use version 3, and some things I can’t quite grasp just yet. Like defining type policy globally per query name. Here’s my issue, at work we have a search query, an offset based one, and it’s used in very different UI contexts. We have views that are table like with lots of paged data, and then we have infinite scroll type of feeds.
I have found that a single type policy for query search does not really quite support this. Here is our type policies:
The hacky part here is that we are using field alias:
pagedSearch: search(input: $input from: $from)
for search query and pushing offset variable to the keyArgs of paged queries, so user gets fresh results when they change the table’s page.if (context.field?.alias?.value === 'pagedSearch') { keyArgs.push('from'); }
This does get the job done for now, but I want to know how would one implement a single type policy for these type of queries?I have asked this question on stack overflow a while a go, but no answers yet https://stackoverflow.com/questions/68157953/what-kind-of-apollo-client-type-policy-for-infinite-scrolling-and-paged-results