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
pollInterval not working with offset-based fetchMore pagination #1087
Comments
From reading the docs it seems like If so that's a pretty big caveat to leave out. The documentation should probably make this a lot clearer. |
@SachaG yeah, it won't work, because the polling actually overwrites the result from the fetchMore (as you pointed out). This won't be any different with cursor-based pagination. In order to make polling and fetchMore work together, we'd need to add some logic that updates the variables of the base query so that when the polling happens, the new query contains all the data, including the one that For now, my recommended workaround would be to use subscriptions for that update, if possible. |
Oh ok, thanks for the added details. I assumed since you can get the server to return a new cursor value, that new value would be used for polling instead of the original cursor. One of my biggest gripes with Meteor (and one reason for migrating to Apollo in the first place) was that it never offered a good solution for pagination, so I really hope Apollo won't have the same problem! I'll look into subscriptions, although I was hoping to use polling to reduce server load for performance reasons. But that's another discussion I guess. |
I was talking about this issue with @tmeasday and he suggested not using I haven't tried this yet, but it seems like maybe it could be a better solution for pagination than |
Still it may be the only way to get the best performance. |
A more declarative alternative would be to offer an API to specify a query to run + patch in when the variables change (so you call |
Could that be built on top of fetchMore? |
Hmm, I guess, but it would be weird if But yeah, I think an API like this would be great: obs = client.watchQuery({
query: ...,
options: ...,
fetchMore: {
query: (oldOptions, newOptions) => ...,
variables: (oldOptions, newOptions) => ...
updateQuery(/* as before */)
}
})
// and instead of calling `obs.fetchMore`, you'd call
obs.setOptions(...) // or `setVariables` Then Rather than an imperative "fetch more now and this is how to do it", it's a declarative "if you need to fetch more, here's how to do it" |
Just thinking out loud: if we didn't want to add complexity to Apollo's client APIs, could we do this on the server? In other words, if the query returned
It would then be the resolver's job to return the correct new |
By the way, why use fetchMore in the first place here? Why not just change the offset and fire a new query? Since the polling will eliminate the benefits of incremental loading anyway. |
Sure I guess I just don't consider this a case of pagination - since pagination is usually used to load only some data and not to reload everything. So IMO semantically pagination with polling doesn't really make sense |
Maybe it's because I'm coming from Meteor but I don't see why it doesn't make sense. If you have a paginated forum homepage like the Meteor forums, and someone edits one of the post titles, wouldn't you want that title to be updated without waiting for a reload? |
Sure, but in that case you don't get any benefit from using fetchMore to load the new page, since you're going to reload the whole thing right afterwards anyway. I guess pagination is a misleading term - it should be something like "loading partial new data" or similar. |
Maybe there's a small benefit in that the new data loads faster? |
I'm just trying to explain that I'm just using fetchMore because that's what's in the documentation and at no point is it written that it doesn't work with polling. You can't expect users to understand in advance the pros and cons of every possible pattern, we have to trust the docs at some point… So it's just not super helpful to be told that I'm not supposed to be using |
By the way, another thought: what if polling happened at the store level (so for the entire store)? This way queries wouldn't need to handle it anymore, and could simply receive updated documents from the store? |
Oh I'm not criticizing your approach, just arguing against adding a new feature for polling and pagination. I agree we should at least update the docs. At the end of the day it's up to Jonas anyway. |
OK, so I got rid of And also removing statements like |
Related question: what would be a good way to trigger a callback once my container is done loading more data? |
Doesn't Can't think of any great ways but |
Yeah I meant in the declarative case. I'll look into |
I haven’t read the full issue, but this is a concept that I’ve been thinking about abstractly. Not sure if this has been mentioned, but I really think that we need a separate data structure for pagination separate from the data we initially requested. This may potentially be solved by client fields. |
I think the answer here is that it doesn't make sense to use fetchMore and polling together at the same component. Apart from warning people when they do that, but not do anything else. In the future, we may implement a separate structure for connections, which could make polling work on paginated lists. |
Closing because we don’t currently intend to make polling work with fetch more. The implementation would just be to difficult 😣 However, we recognize the use case! Hopefully we can introduce a better to implement pagination soon. |
A little late, but one case for the Just changing one variable doesn't work, as the start of the list is than being removed. And polling is wished for to update information along the way. It can for example happen that an item moves from "page 2" to "page 1" when sorting on a After reading this issue, I'm still unsure what the way to go would be here. |
Wouldn't this issue actually be solvable when a |
I have an infinite list that uses My API is already implementing the cursor approach where I send the id of the latest item and it'll give me the next 10.
So far it works. I can't quite decide if it's a clever solution or just a giant hack. Maybe it's both 😄 @smeijer 's suggestion would 💯 fix this problem. Hope it gets implemented some time! |
Unfortunately I don't have the ability to query for all changed items in a list. Instead I'd need to increment the limit of my polling query based upon the scroll and subsequent calling of fetchMore. This could amount to a huge list which I'd need to merge into the cache... doable but kind of intensive. |
I've solved it by:
As result I have smooth infinity scrolling with polling for keeping info updated |
@helfer, is there a reason why we couldn't trigger the Even now we have hooks, this pattern keeps frustrating me. I just need a paginated list, in combination with a poll to refresh the "current view". |
My solution for this: const defaultPerPage = 10;
const poolInterval = 1000;
function SomeComponent() {
const [currentPage, setPage] = useState(0);
const [perPage, setPerPage] = useState(defaultPerPage);
const [refetchTrigger, setRefetchTrigger] = useState(0);
React.useEffect(() => {
const interval = setInterval(() => setRefetchTrigger(Math.random()), poolInterval);
return () => clearInterval(interval);
}, []);
const queryResult = useQuery(SOME_QUERY, {
variables: { first: defaultPerPage, skip: 0 },
});
React.useEffect(() => {
queryResult.fetchMore({
variables: {
first: perPage,
skip: currentPage * perPage,
},
updateQuery: (prev, { fetchMoreResult }) => {
return fetchMoreResult || prev;
},
});
}, [queryResult.fetchMore, currentPage, perPage, refetchTrigger]);
return (
<div>
<button onClick={() => setPage(currentPage + 1)}>Next</button>
<pre>{JSON.stringify(queryResult.data, null, 2)}</pre>
</div>
);
} |
Workaround // Poll interval that works with pagination
useEffect(() => {
const intervalId = setInterval(() => {
const total =
(queryResult.data?.countSessions.edges.length || 0) +
queryResult.variables.first!;
queryResult?.refetch({
...queryResult.variables,
first: total
});
}, 15_000);
return () => clearInterval(intervalId);
// eslint-disable-next-line
}, [
...Object.values(queryResult.variables).flat(),
queryResult.data?.countSessions.pageInfo.endCursor
]); |
I'm using
fetchMore
to implement offset-based pagination as suggested in the docs, and that works. But when I addpollInterval
to refresh the data, it wipes out all the newly loaded data every time the interval triggers.What am I doing wrong?
The text was updated successfully, but these errors were encountered: