Apollo-client: queryVariables returned by updateQuery contain stale values

Created on 8 Jan 2017  路  6Comments  路  Source: apollographql/apollo-client

While implementing pagination for a graphQL+React app, the updateQuery method passed as an option to fetchMore also contains queryVariables as an argument, which are returned by fetchMore. However, queryVariables contain the variables for the original query, not the updated ones passed to fetchMore. This seems counter-intuitive, as one would expect the updated variables to be returned by the query.

Here is a replication of the code in the Githunt example:

const ITEMS_PER_PAGE = 10; const withData = graphql(FEED_QUERY, { options: props => ({ variables: { type: ( props.params && props.params.type && props.params.type.toUpperCase() ) || 'TOP', offset: 0, limit: ITEMS_PER_PAGE, }, forceFetch: true, }), props: ({ data: { loading, feed, currentUser, fetchMore } }) => ({ loading, feed, currentUser, fetchMore: () => fetchMore({ variables: { offset: feed.length, }, updateQuery: (prev, { fetchMoreResult, queryVariables }) => { // Here, queryVariables contains {offset: 0, limit: 10} // even though the value of offset is updated above and should be equal to feed.length. if (!fetchMoreResult.data) { return prev; } return Object.assign({}, prev, { feed: [...prev.feed, ...fetchMoreResult.data.feed], }); }, }), }), });

Most helpful comment

Thanks for your comments @helfer. I tried both react-apollo 0.8.3 and the most recent release of 1.1.1 and both have the same issue of having queryVariables returning the original data. I was able to get the variables passed to fetchMore in the closure. I would think it might be better not having queryVariables at all since both the original variables and the ones used in fetchMore are available in updateQuery scope. I will add a short description to the use-cases and how I handled the scenario later today. Thanks again!

All 6 comments

@nupurgrover I'm 99% sure that that's intentional, because you already have access to the fetchMore variables in that scope, so updateQueries provides you with the variables of the original query, which you do not have access to in that scope (at least not easily).

@helfer I don't completely agree, since I can easily access the original variables in the updateQuery method:

  props: ({ data: { loading, feed, currentUser, fetchMore, variables } }) => ({
    loading,
    feed,
    currentUser,
    fetchMore: () => fetchMore({
      variables: {
        offset: feed.length,
      },
      updateQuery: (prev, { fetchMoreResult, queryVariables }) => {
        console.log('Logging original variables here: ', variables);

        if (!fetchMoreResult.data) { return prev; }
        return Object.assign({}, prev, {
          feed: [...prev.feed, ...fetchMoreResult.data.feed],
        });
      },
    }),
  }),

I stumbled across this use case while trying to pass a prop to the component which was dependent on the value of the query variables, and assumed that the value of the variables would be overriden by the new variables the same way the value of feed can be overriden above. But that is not the case and since the value in queryVariables is not updated, it becomes impossible to pass a variable-dependent prop to the component.

@nupurgrover the problem with accessing the original query variables is that you would have to keep track of every set of variables the query was ever called with, and then associate those with each invocation of updateQueries. That seems like a lot harder to do than knowing the set of variables that fetchMore was called with. However, if this is a use-case that many people find useful, we could definitely add the fetchMore variables as well.

I am working on a scenario which is very similar to what was described in https://github.com/apollographql/apollo-client/issues/948, where I have a timeline based data, as user clicks on left or right arrow I need to call fetchMore to fetch more data based on different time ranges concurrently. Since the fetchMore results will not arrive in the same order as they are initiated, I was totally counting on queryVariables to figure out which set of fetchMore data has arrived.

I was too surprised to find queryVariables always return the original variables as well. Especially when documentation states: "queryVariables are the variables that were used when fetching more data." is misleading.

@HeatherZh I think this may be due to a bug. The variables you get should be the variables of the base query at the time the fetchMore request was made. Is that not the case?

As for knowing which fetchMore result is being returned, I think using a closure is probably the easiest and most flexible option. You could technically also use that to know what the query variables were at the time fetchMore was called (unless I'm missing something).

Your use case seems to be a common scenario for working with timeline data, so we'd like to make it as easy as possible. Could you add a short description to the use-cases issue on react-apollo so we can keep track of it? Thanks!

Thanks for your comments @helfer. I tried both react-apollo 0.8.3 and the most recent release of 1.1.1 and both have the same issue of having queryVariables returning the original data. I was able to get the variables passed to fetchMore in the closure. I would think it might be better not having queryVariables at all since both the original variables and the ones used in fetchMore are available in updateQuery scope. I will add a short description to the use-cases and how I handled the scenario later today. Thanks again!

Was this page helpful?
0 / 5 - 0 ratings