Skip to content
This repository was archived by the owner on Apr 13, 2023. It is now read-only.

Skip & data.refetch #289

Closed
bkniffler opened this issue Oct 24, 2016 · 29 comments
Closed

Skip & data.refetch #289

bkniffler opened this issue Oct 24, 2016 · 29 comments

Comments

@bkniffler
Copy link

bkniffler commented Oct 24, 2016

Skipping a query will not allow to refetch data, since the refetch function will be undefined. Is this a bug or by design?

@graphql(gql`
  query verify($auth: String!) {
    user: verify(token: $auth) {
      user {  id, name }
    }
  }
`, {
  props: ({ ownProps, data }) => {
    console.log(data.refetch); // undefined if skipped
    return data;
  },
  options: () => ({
    skip: !localStorage.getItem('auth'),
    variables: { auth: localStorage.getItem('auth') },
  }),
})
@heysailor
Copy link

I agree this could be made more helpful. I may be missing something in the react apollo docs, so I've opened a SO question: http://stackoverflow.com/questions/40296800/how-do-you-dynamically-control-react-apollo-client-query-initiation

Ideally, it'd be great to be able to dynamically control when a query is initiated.

Apparently skip is not evaluated in response to prop updates.

For example, I have a map which loads different types of data. I'd like to only load one type on component mount.

At the moment, if I wrap the map component in multiple apollo containers, all data types are loaded on mount or prop change.

It would be handy to have a way to prevent the initial query for each wrapping container from being fired - but to be able to trigger it later, say in response to user input.

@jbaxleyiii
Copy link
Contributor

@bkniffler, @heysailor it currently is by design because the idea is you can't refetch something not fetched? I can see the usefulness in supporting it though! Would you be able to open a PR?

@svrcekmichal
Copy link

svrcekmichal commented Oct 28, 2016

It would be also awesome if we can pass some option, for example onlyInitialFetch or disableFetchingOnVariableChange so we can create component which will not listen to variable changes. This way we can make big fetch with graphql HOC and then define custom fetching on props change with fetchMore

@heysailor
Copy link

That's a great idea. Two new would handle it:

disableAutoQuery in the query options to prevent initial and subsequent queries in response to mount and prop/variable changes

fetch as opposed to refetch - supplied as a prop to the wrapped component only if disableAutoQuery is true. Which is really only renaming the usual fetch prop.

@tmeasday
Copy link
Contributor

tmeasday commented Nov 1, 2016

If you want that level of control over when the query runs etc, I wonder if you should just use withApollo and run the query yourself?

@heysailor
Copy link

Yes, that's what I'm doing currently. But it seems like a handy feature that would allow more use of the container style.

On Wed, Nov 2, 2016 at 10:01 AM +1100, "Tom Coleman" notifications@github.com wrote:

If you want that level of control over when the query runs etc, I wonder if you should just use withApollo and run the query yourself?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@svrcekmichal
Copy link

@tmeasday It is fine to use withApollo but you have to do quite a lot of logic myself and don't know if it will be cleaner
@heysailor can you create some snippet on gist with your usage?

@heysailor
Copy link

heysailor commented Nov 2, 2016

Hey Michel, I'm not doing anything fancy but doing a direct call to 'apolloClient.query({ options })' - documented in the API reference of the Apollo client. It returns a promise with a data parameter which I parse in the same way you can parse the Apollo result from a containerised call.
In order to avoid data duplication and my fetched data being out of date, I pull out only the IDs of the data I need and keep them as an array. Individual child items in Apollo containers then get the data they need. Sounds awful, but with batching and client caching, there are minimal server requests.
I'd still rather be able to simplify it by passing data directly from an Apollo container parent though!

@heysailor
Copy link

heysailor commented Nov 4, 2016

Note that with the release of Apollo client version 0.5, the query merging style of query batching is being discontinued. You need a batching capable server to do it. It would therefore now be better to avoid many individual components making queries as I was using earlier.

@helfer
Copy link
Contributor

helfer commented Nov 4, 2016

@heysailor We're still planning on publishing query merging in a separate package. If making lots of individual components with small queries made your code easier to understand and maintain, I would stick to that, and not make things more complicated just because there's currently no package that does batching for you if the server doesn't support it.

@cannoneyed
Copy link

@helfer Any word on the concept of query merging?

@machard
Copy link

machard commented Nov 24, 2016

there is an other issue with skip.
When reseting the store https://github.com/apollostack/apollo-client/blob/master/src/core/QueryManager.ts#L633 will actually run all queries without checking 'skip' result first.

Actually when skip change to false, graphql HoC unsubscribes from the queryObservable (https://github.com/apollostack/react-apollo/blob/master/src/graphql.tsx#L286). So I see 2 options:

What do you think?

I did proposition 2 pull requests : #342

@johhansantana
Copy link

johhansantana commented Sep 11, 2017

is this still an issue? how can you disable the first automatic fetch while being able to fetch it later on? skip doesn't work

@bkniffler
Copy link
Author

We've been discussing a bit on this topic here: #196

The best way currently to skip the query while allowing to refetch is to use fetchPolicy

@graphql(
  gql`
    query xyz {
      xyz {
        id
      }
    }
  `,
  {
    options: ({ skip }) => ({
      fetchPolicy: skip? 'cache-only' : undefined,
    }),
  },
)

Basically this will force to run graphql agains the cache, for example if you got a detail view that gets an id prop, which may be empty if you want to create a new object

@graphql(
  gql`
    query xyz($id: String) {
      xyz(id: $id) {
        id
      }
    }
  `,
  {
    options: ({ id }) => ({
      fetchPolicy: !id? 'cache-only' : undefined,
      variables: { id }
    }),
  },
)

@zackify
Copy link

zackify commented Sep 14, 2017

@bkniffler still don't see how this helps for my situation. If you pass an id to the component but don't want it loaded until refetch is called, this wont work, since the id is there. My use case is that I don't want to generate a signed download URL from google cloud storage unless a user clicks the download button. Can't find an easy way to only query after a user clicks :/ unless I do some weird hacky thing.

@bkniffler
Copy link
Author

bkniffler commented Sep 14, 2017

Can't you do something like:

<Download _id={id} />

@graphql(
  gql`
    query xyz($id: String) {
      xyz(id: $id) {
        id
      }
    }
  `,
  {
    options: ({ id }) => ({
      fetchPolicy: !id? 'cache-only' : undefined,
      variables: { id }
    }),
  },
)
class Download extends Component {
  triggerRefetch(){
    refetch({ id: this.props._id })
  }
}

I agree its hacky and I'd wish for some proper way to control skipping.

@zackify
Copy link

zackify commented Sep 14, 2017

@bkniffler oh okay, I thought options only took in the component props, not the refetch variables.

@bkniffler
Copy link
Author

Urgh, I think you're right. I think you're better off using the vanilla apollo client that you get with @withapollo. By the way, why aren't you using mutations?

@zackify
Copy link

zackify commented Sep 14, 2017

@bkniffler I'm not changing anything. Just loading data, why would it be a mutation?

@dstendardi
Copy link

dstendardi commented Oct 31, 2017

Hello !

It seems you can achieve this using directive @skip

export default graphql(gql`
    query SearchQuery($query: String, $first: Int, $skip: Boolean!) {
        search(query: $query, first: $first) @skip(if: $skip) {
            edges {
                ....
            }
        }
    }
`,  { options: {variables: { skip: true } }})(Input);

Initial query wont be triggered, but refetch will be available :)

@nfantone
Copy link

nfantone commented Nov 20, 2017

I encountered a similar situation today. I'm still new to the GraphQL scene, but in my mind, I keep wondering how is this not the default case for every query. Isn't most data requested after some kind of async user interaction? Clicking a link, a button, filling an input.

A basic search/filter scenario is the simplest use case I can think of where this is most evident. Think about a set of results being displayed after submitting a search. Is there any "proper" approach to handling this type of situations, thinking in GraphQL?

@jbaxleyiii
Copy link
Contributor

@dstendardi That is a great example! We should add it to the docs on how to achieve this! I think we can also support a variant / policy towards skipping that may make this easier as well? I'm torn because everything (that I have seen) is possible with today's skip process.

@nfantone I think GraphQL is used both in response to inputs and as a way to fetch data for the app to even render. So it is needed to be able to be called on demand and load initially

@nfantone
Copy link

nfantone commented Nov 20, 2017

I think GraphQL is used both in response to inputs and as a way to fetch data for the app to even render.

@jbaxleyiii Well... that's not entirely true. At least, not always. Sometimes you find yourself wanting to just display components that would allow the user to provide some input that may, potentially, trigger some data fetching. Or not. So the rendering capabilities of GraphQL don't even play a part until the user interacts with the UI.

@jbaxleyiii
Copy link
Contributor

@nfantone I agree! That's what I meant by I think GraphQL is used both in response to inputs

luqmaan added a commit to cityofaustin/ctxfloods that referenced this issue Mar 15, 2018
Instead get the `<FloodsRoutes>` component to fetch `currentUser` after logging in.

Had to switch from apollo's skip operation https://www.apollographql.com/docs/react/basics/queries.html#graphql-skip, to the `@skip` directive so that the component would be able to call `data.refetch()`. Apollo doesn't pass data to the component with the skip operation. apollographql/react-apollo#289 (comment)
@reggie3
Copy link

reggie3 commented May 21, 2018

@dstendardi Is it possible to send a different set of variables to query using your technique? From what I can tell, I'm stuck with the variables that are hard coded in it when you do the composition.

@ghost
Copy link

ghost commented Jul 16, 2018

@dstendardi ´s solution works fine for the use case of skipping initially and calling the query on button press!

@mark-atkinson-unmind
Copy link

This doesn't work if you don't want to fire an initial query to your GraphQL endpoint. I don't really see this as a viable solution.

@jaydenseric
Copy link
Contributor

For people still wondering the best way to load a <Query> on demand, here is a <DeferredQuery> component that seems to work ok: https://gist.github.com/jaydenseric/5ff7ebeafca16da32a9fdb9055e99e1e

@alexandrzavalii
Copy link

alexandrzavalii commented Apr 27, 2020

created a custom hook with useState as a workarround.
In my case onCompleted was fired, however data from useQuery was not updated

const useTrackingData = (id) => {
  const [trackingData, setTrackingData] = useState();
  const { refetch } = useQuery(
    QUERY,
    {
      variables: { param: id },
      skip: !id,
      onCompleted: (data) => {
        if(data) setTrackingData(data)
      }
    },
  );

  const fetchTracking = async (param) => {
    const {data} = await refetch(param)
    setTrackingData(data)
  }

  return [trackingData, fetchTracking]
}

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests