-
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
fetchMore's updateQuery option receives the wrong variables #2499
Comments
Uploaded repro here: https://github.com/jamesreggio/react-apollo-error-template/tree/repro-2499 |
So, my hack above doesn't actually resolve the problem :-/ To be clear, there are two — and possibly three — related issues at play here:
|
@jamesreggio as always, thank you for the thoughtful research and repo! I'll get on it ASAP! |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions to Apollo Client! |
I think I've run into this same issue while trying to do pagination. I'm trying to do fetchMore({
variables: {
page: variables.page + 1
}
}, ...); where |
Any update ? I also notice that List of the packages I use :
const { data } = this.props;
<FlatList
data={data.feed}
refreshing={data.networkStatus === 4}
onRefresh={() => data.refetch()}
onEndReachedThreshold={0.5}
onEndReached={() => {
// The fetchMore method is used to load new data and add it
// to the original query we used to populate the list
data.fetchMore({
variables: { offset: data.feed.length + 1, limit: 20 },
updateQuery: (previousResult, { fetchMoreResult, queryVariables}) => {
// Don't do anything if there weren't any new items
if (!fetchMoreResult || fetchMoreResult.feed.length === 0) {
return previousResult;
}
return {
// Concatenate the new feed results after the old ones
feed: previousResult.feed.concat(fetchMoreResult.feed),
};
},
});
}}
/> |
Encountering the same issue here. |
In case this might interest someone, I'm currently working around this by passing the necessary paging parameters combined with an updater function from the child component calling the const withQuery = graphql(MY_LIST_QUERY, {
options: ({ pageSize, searchText }) => ({
variables: {
pageIndex: 1,
pageSize,
searchText: searchText
}
}),
props: ({ data }) => ({
data: {
...data,
fetchMore: ({ pageIndex, updatePageIndexFn }) => {
const nextPageIndex = pageIndex + 1;
updatePageIndexFn(nextPageIndex);
return data.fetchMore({
variables: {
pageIndex: nextPageIndex
},
updateQuery: (previousResult, { fetchMoreResult }) => ({
items: [
...previousResult.items,
...fetchMoreResult.items
]
})
});
}
}
});
class ListComponent extends React.Component {
constructor(props) {
super(props);
this.pageIndex = props.data.variables.pageIndex;
}
handleLoadMore = event => {
event.preventDefault();
this.props.data.fetchMore({
pageIndex: this.pageIndex,
updatePageIndexFn: index => this.pageIndex = index
});
}
render() {
return (
<React.Fragment>
<ul>{this.props.items.map(item => <li>{JSON.stringify(item, null, 2)}</li>)}</ul>
<a href="" onClick={this.handleLoadMore}>Load More</a>
</React.Fragment>
);
}
}
const ListComponentWithQuery = withQuery(ListComponent); edit: What I initially assumed would work is this: const withQuery = graphql(MY_LIST_QUERY, {
options: ({ pageSize, searchText }) => ({
variables: {
pageIndex: 1,
pageSize,
searchText: searchText
}
}),
props: ({ data }) => ({
data: {
...data,
fetchMore: () => data.fetchMore({
variables: {
pageIndex: data.variables.pageIndex + 1
},
updateQuery: (previousResult, { fetchMoreResult }) => ({
items: [
...previousResult.items,
...fetchMoreResult.items
]
})
})
}
}); |
Is this intended? |
still got this issue.. hard to do pagination with refetch now |
Any chances to fix that 5-months old issue? It seems to be quite significant disfunction. A bit funny considering an activity of Apollo team on various React confs and theirs effort to promote Apollo integrations... |
same here, I'm using |
@jbaxleyiii any update on this at all please? Kind regards, |
I am amazed how such a critical issue on Apollo is not being addressed! How are current apollo users handling cursor based pagination? I am having hard time coming with a hack to solve this issue. Can anyone suggest a way to get cursor based pagination working, at least for now? |
I'd love to see some help/movement on this issue as well. Using latest (2.3.1) apollo-client. Attempting to use the new component, and implement similar to mentioned above. I've been able to get the single 'load more' button to work per the examples on the 'pagination' page, and get those results to either add to the end of the previous result set (infinite scroll style) or replacing the previous result set (Next page style). However, as soon as I try to implement numbered pages, and try passing in a page number to do calculations with to configure the actual offset, i continually get |
@ZiXYu did your comment intend to say you I was considering it as a path to take, but hadn't seen much in the way of documentation on it. |
This issue still happening on |
Same issue on edit: There was no problem with Apollo, the bug happened because we were mutating an object that was passed by props. So it was the same object, mutated, but still the same reference. |
I'm seeing the same issue when I invoke a You can also see it here in action:
spectrum.chat runs on [email protected]. |
Same here on 2.4.3 -- I'll try and work around this for now. |
I've just updated my old comment, there was no problem with the Apollo library in my case. After hours of debugging I've discovered that one object was being mutated and expected to trigger a different render. Off course it did not, Apollo thought it was the same object as before. |
Using HOC, it seems to work. The offset variable can be changed with fetchMore. the limit variable i passed in through HOC. https://stackoverflow.com/questions/53440377/apollo-graphql-pagination-failed-with-limit However, using rendered props, i face the same issue whereby the offset variable cannot be changed (and is stuck to the initial offset variable) using fetchmore https://stackoverflow.com/questions/53446467/offset-variable-does-not-update-with-apollo-fetchmore it would be excellent if there is a fix to allow the rendered props method to work correctly. After all, i understand rendered props is the preferred approach going forward. Thank you |
@hwillson It's still not working for me. |
I had this problem and found a workaround for the issue. Instead of calling the fetchMore function received from the Query component, I would:
This worked flawlessly for me. |
Still having a similar issue: const nextPage = (skip: number, currentSearch = "") =>
fetchMore({
variables: { houseId, skip, search: currentSearch },
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult || !prev.allCosts || !fetchMoreResult.allCosts)
return prev
return Object.assign({}, prev, {
allCosts: {
...prev.allCosts,
costs: [...prev.allCosts.costs, ...fetchMoreResult.allCosts.costs],
},
})
},
}) I call this function when reaching the bottom of the page. It works perfectly when the search variable remains as "", when reaching the bottom the skip variable changes and new data is refetched and works great. However when i change the search variable and refetch the original query, and then try and paginate, i get an "ObservableQuery with this id doesn't exist: 5" error, I had a look in the source code/added some logs and it seems that apollo is using the old queryId (from the query where the search variable was "") for the fetchMore. I can see the data being successfully fetched in the network with all the correct variables but saving it back to the cache seems to be where it's erroring, am i missing something with this implementation? Note: I am using react-apollo-hooks, which i know isn't production ready or even provided by apollo-client right now, but from what i've seen it looks like its something to do with the fetchMore API Note: @FunkSoulNinja solution works for me, however would be nice to be able to use the provided API for this kind of feature |
This is still an issue on The workaround by @FunkSoulNinja above works, but becomes convoluted – here's a more complete example of how it goes together: import React from "react";
import { Query, withApollo } from "react-apollo";
import gql from "graphql-tag";
const QUERY = gql`
query someQuery(
$limit: Int
$offset: Int
) {
someField(
limit: $limit
offset: $offset
) {
totalItems
searchResults {
... on Something {
field
}
... on SomethingElse {
otherField
}
}
}
}
`;
const SearchPage = props => {
const { client } = props;
const defaultVariables = {
limit: 10,
offset: 0,
};
return (
<Query query={QUERY} variables={defaultVariables}>
{({ loading, error, data, updateQuery }) => {
const { someField } = data;
const { searchResults, totalItems } = someField;
return (
<>
<ResultList results={searchResults} total={totalItems} />
<Button
onClick={async () => {
const nextVariables = Object.assign({}, defaultVariables, {
offset: searchResults.length,
});
const prevVariables = Object.assign({}, defaultVariables, {
offset: searchResults.length - defaultVariables.limit,
});
const [next, prev] = await Promise.all([
client.query({
query: QUERY,
variables: nextVariables,
}),
client.query({
query: QUERY,
variables: prevVariables,
}),
]);
const updatedData = {
...prev.data,
someField: {
...prev.data.someField,
searchResults: [
...prev.data.someField.searchResults,
...next.data.someField.searchResults,
],
},
};
updateQuery(
(_prev, { variables: prevVariables }) => updatedData
);
client.writeQuery({
query: QUERY,
variables: nextVariables,
data: updatedData,
});
}}
>
Load more
</Button>
</>
);
}}
</Query>
);
};
export default withApollo(SearchPage); |
@MarttiR You could also use the client object from the Query component render prop function without having to use the withApollo HOC. |
@FunkSoulNinja can you post the full solution please. I'm having the same issue. struggling to put together your solution |
Still have the same issue( |
There is a workaround for this... or maybe the solution
like below... const GET_ITEMS = gql`
query GetItems($skip: Int, $take: Int, $current: Int) {
getItems(skip: $skip, take: $take, current: $current) @connection(key: "items") {
id
data
type
list {
id
name
}
}
}
`;
let fetch_options = { skip: 0, take: 2, current: 0 };
export const Pagination: React.FC<Props> = () => {
const { called, loading, data, fetchMore } = useQuery(GET_ITEMS, {
variables: fetch_options,
// fetchPolicy: "cache-and-network", // Do not set
});
if (called && loading) return <p>Loading ...</p>;
if (!called) {
return <div>Press button to fetch next chunk</div>;
}
return (
<div>
<Button
onClick={(e: any) => {
fetch_options = {
...fetch_options,
current: fetch_options.current + 1,
};
fetchMore({
variables: fetch_options,
updateQuery: (
previousQueryResult,
{ fetchMoreResult, variables }
) => {
if (!fetchMoreResult) return previousQueryResult;
return {
getItems: previousQueryResult.getItems.concat(
fetchMoreResult.getItems
),
};
},
});
}}
>
Fetch Next
</Button>
);
}; |
@RyotaBannai That didnt work for me :/ |
It's 2021 and I am having this issue too. My work around was to use a ref to keep track of the cursor // wrap useQuery
export function usePaginatedQuery(query, options, updateQuery) {
const cursorRef = useRef(0) // use a ref since updating it does not cause a re-render
const results = useQuery(query, options)
// handle pagination
const getMore =
(() =>
results
.fetchMore({
variables: { cursor: ++cursorRef.current },
updateQuery,
})
return {
...results,
getMore,
}
} |
+1 |
+1 Using the HOC it works fine, the problem is trying to use the fetchMore from the Query component... |
Actually, my problem was that I was passing the variables in the wrong format and It seems that since the variables were in the wrong format he was just reusing the old input... |
+1 |
hi. using |
Issue still exists in |
Anyone found a reliable solution ? |
Check the variables object you are passing to fetchMore function. It could be the possible issue, you can somehow easily miss... I've encounter this issue recently on @apollo/client >3.4.10. But there was no problem with the apollo... I was passing a bad variables object to fetchMore function. But I was passing to fetchMore function the following variables object: { variables: { first: 10, cursor: "xyzCusro.string" } } |
Cursor based pagination is working fine, but offset based pagination is glitched... |
Issue still exists on const loadMoreItems = async () => {
if (data && data.getCollection.itemsNextPageToken) {
fetchMore({
variables: {
id,
itemsPageSize,
itemsPageToken: data.getCollection.itemsNextPageToken,
},
updateQuery: (prevQueryResult, newQueryResult) => ({
getCollection: {
...newQueryResult.fetchMoreResult.getCollection,
items: uniqBy(
prevQueryResult.getCollection.items.concat(
newQueryResult.fetchMoreResult.getCollection.items,
),
'id',
),
},
}),
})
}
} |
The
updateQuery
method passed toObservableQuery.fetchMore
is receiving the original query variables instead of the new variables that it used to fetch more data.This is problematic not so much because it breaks the
updateQuery
method contract (which is bad, but clearly doesn't seem to impact many people), but rather because when the results fromfetchMore
are written to the store inwriteQuery
, the old variables are used. This cases issues, say, if you were using an@include
directive and the variable driving its value changes.Repro is available here: https://github.com/jamesreggio/react-apollo-error-template/tree/repro-2499
Expected behavior
nextResults.variables
showspetsIncluded: true
and thepets
list gets displayed.Actual behavior
nextResults.variables
showspetsIncluded: null
and thepets
list is not displayed because the write never happens due to the@include
directive being evaluated with the old variables.Version
The text was updated successfully, but these errors were encountered: