React-apollo: Support a way to access previously used variables on "refetch()"

Created on 18 Aug 2017  路  10Comments  路  Source: apollographql/react-apollo

Hi all,

This may be a question or a suggestion :P Because maybe there is a way to do this which I'm not finding at any docs.

The really really short version of this is that I would like data.refetch(variables) to support a function instead of the variables, allowing that function to generate new variables based on the last used variables. Something like this

data.refetch(oldvars => ({ ...oldVars, a: 23 }) )

Now the long version to understand the usecase/context and see if my request makes sense :)

I had this case which seems quite common in a CRUD app:

  • a list of entities (users in this case)
  • with a searchbox to filter those entities (server-side)
  • paginated

I'm managing this with a single graphql query which receives parameters for both things: page number, and search filter (questionably called "query" :S)

Here is a query ussage:

query UsersQuery($page: Int = 1, $query: String) {
    users(page: $page, size: 14, query: $query) {
        ...
    }
}

So my mental model of this is that

  • there is single query
  • a user searching means to fetch again that query but with different variables: 1) the new query, 2) reseting the page nr to 1 (doesn't make sense to search to the current page)
  • a user changing the page means to fetch again with different variables: 1) different page nr, 2) but... keep the same search query as the latest performed !

So, in my mind I would like to model this as the following abstract functions

const changePage = newPage => refetch(oldVars => ({ ...oldVars, page: newPage })
const search = filter => refetch(() => ({ query: filter, page: 0 })

But I didn't find a way to access that oldVars lets say, and then to move this responsibility to remember the lastly used vars from apollo (which already has this data) and pass it over again to me.

I was able to implement it but by managing that state "manually" myself, using the component's state.
Here is the example.
I coded some utilities to be able to write the props injection as declarative as possible and to ressemble to my previous definitions of the functions.

@graphql(USERS_QUERY, {
  options: () => ({ variables: { page: 1 } }),
  ...gqlProps({
    changePage: (query, page) => reQuery({ page, query }),
    search: query => reQuery({ query })
  })
})
export default class Users extends React.Component {

  state = { query: undefined, page: 1 }

  render() {
    const { data, user } = this.props
    return (
        <div>
          <SearchBox onSearch={::this.onSearch} />
          <Table users={data.users} user={user} />
          <Pager data={data.users.paging} onPageChanged={::this.onPageChanged} />
      </div>
    )
  }
  onSearch(query) {
    this.setState({ query })
    this.props.search(query)
  }
  onPageChanged(page) {
    this.setState({ page })
    this.props.changePage(this.state.query, page)
  }
}

As you see here I want to keep my component decoupled from graphql, so I inject high-level functions to search(query), and changePage(currentQuery, newPage).
That last one arg "currentQuery" I believe that could be avoided if I can manage reusing variables outside of the component.
Also see that I needed to keep the state of the query & page at component level to access it later (I have a third case that I've omitted here, to make it easier to understand which is a component to create a new user. In that case I want to refetch with the same query and page, that's why I'm also "remembering" the current page in the component store).

Here is the code of the utilities gqlProps and reQuery.

export const gqlProps = spec => ({
  props: ({ data, ...others }) => ({
    ...others,
    data,
    ...specToProps(spec, data)
  })
})

const specToProps = (spec, data) => Object.keys(spec).reduce((acc, key) => ({
  ...acc,
  [key]: (...args) => spec[key](...args)(data)
}), {})

export const reQuery = newVariables => ({ fetchMore }) => fetchMore({
  variables: newVariables,
  updateQuery: replaceUpdateQuery
})

export const replaceUpdateQuery = (_, { fetchMoreResult }) => fetchMoreResult

As you can see I actually had to implement this using fetchMore and not refetch. Which I'm not pretty sure it is the best way (I'm seeing in apollo chrome extension that it creates many watched queries on every page changed, or search, that might be a problem ???)

Intended outcome:

Ok, so if you followed me up to this point, than many thank you !!
The thing is that it would be really cool to get rid of.. basicaly all of this :) but

  • the component storing variables for further refetchs
  • the need of injecting customs functions to "adapt" the fetchMore to a simpler function.

Is that possible ?
I'm thinking of something on a solution with a refetch like this

@graphql(USERS_QUERY, {
  options: () => ({ variables: { page: 1 } })
})
export default class Users extends React.Component {

  render() {
    const { data: { refetch }, user } = this.props
    return (
        <div>
          <SearchBox 
               onSearch={filter => refetch(() => ({ query: filter })}
          />
          <Table users={data.users} user={user} />
          <Pager data={data.users.paging} 
                onPageChanged={newPage => refetch(oldVars => ({ ...oldVars, page: newPage })}
           />
      </div>
    )
  }
}

This of course needs a new form of data.refetch() function.

Actual outcome:

Current data.refetch() just receives variables (http://dev.apollodata.com/react/api-queries.html#graphql-query-data-refetch)
It would be great to expect a function (oldVars) => newVars

I tried a couple of things to see if I was able to get the previous variables

  • inspecting the data.variables always showed the initial query variables (page: 1 set as options while decorating the component). And they never got updated while doing more fetchs
  • The updateQuery(previousResult, ..) from fetchMore jus has the data from the previous result, but not the query+variables performed :(
  • queryVariables from fetchMore are the new variables but not the previous.
  • updateQuery() seems to have access to the previous variables (http://dev.apollodata.com/react/api-queries.html#graphql-query-data-updateQuery) but its semantic is not appropiated for doing refetchs, just for manipulating the cache locally :(

So, am I just missing a way to do this ?
Or do you think that it is ok for the user to handle the state manually by its own ? (I hope you say no :))

Version

Most helpful comment

halo, I would like to see this feature too.
my use case to re fetch on a paged query.

All 10 comments

This issue has been automatically labled because it has not had recent activity. If you have not received a response from anyone, please mention the repository maintainer (most likely @jbaxleyiii). It will be closed if no further activity occurs. Thank you for your contributions to React Apollo!

This issue has been automatically closed because it has not had recent activity after being marked as no recent activyt. If you belive this issue is still a problem or should be reopened, please reopen it! Thank you for your contributions to React Apollo!

halo, I would like to see this feature too.
my use case to re fetch on a paged query.

The same goes for fetchMore.

+1

That would be very useful. In complex scenarios it's getting very hard to pass variables to every query you want to refetch (as it's hard to know in what context they're used)

This issue should not be closed @javierfernandes

+1

+1

For those pursuing a temporary solution, here's what has been working for me: https://github.com/afafafafafaf/apollo-link-watched-mutation.

Was this page helpful?
0 / 5 - 0 ratings