1

How do I get my useQuery() to fire on both first render as well as on subsequent events (e.g. button click)?

From what I've read, when React mounts a component that calls a useQuery for the first time, Apollo automatically executes the query, and if I want to execute that query in response to an event, I can instead use useLazyQuery which returns a function that can be called. What if I want to do both?

Not very sure which part of my understanding of React hooks is incorrect or incomplete, but I've tried with using refetch in useQuery, which definitely allows me to call that function, but doesn't seem to always fire upon component mount (the more confusing thing is that sometimes it does, sometimes it doesn't). I'm also using notifyOnNetworkStatusChange, which I read it needed for refetch to recognise that it should trigger the onCompleted part of the useQuery.

Any help would be much appreciated, I think I don't really understand Hooks at all. Thanks!

P.S. an example of my useQuery is as follows:

const { refetch: refetchGetTaskTypes } = useQuery(GET_TASK_TYPES(["taskId", "taskType"]), {
    notifyOnNetworkStatusChange: true,
    onCompleted: (data) => {
        if (data !== undefined && data.getTaskTypes !== undefined && data.getTaskTypes !== state.taskTypes) {
            dispatch({
                type: 'GET_TASK_TYPES',
                payload: {
                    taskTypes: data.getTaskTypes
                }
            });
        }
    }
});

P.P.S. as an example of refetch sometimes needing to be called to render and other times not, I have another useQuery that is almost identical to the above (with a different query) that also has refetch, but it renders on first load

P.P.P.S. I sometimes see another issue where refetch gets outdated data, and I have to use fetchPolicy: 'cache-and-network' to fix it. It doesn't always happen however

1
  • use useLazyQuery and then call it inside of a useEffect(()->{},[]) which will handle your initial load. Commented Jun 26, 2020 at 5:02

2 Answers 2

6

Use LazyQuery -

const [getTaskType, { loading, data }] = useLazyQuery(GET_TASK_TYPE, {fetchPolicy: 'network-only'}); // fetch policy is to not look for cache and take the data from network only

To get the data on page load -

useEffect(() => {
    getTaskType();
}, []);

To anytime trigger it on button click you can simply call the method. -

<Button                                                
  onClick={()=>getTaskType()}>Click Me!
</Button>
Sign up to request clarification or add additional context in comments.

1 Comment

thank you @Dlucidone! is this the recommended way? also, is it "against" any kind of react-hooks rules? as I understand that in hooks you don't really "call" the hooks per se
0

, but I've tried with using refetch in useQuery, which definitely allows me to call that function, but doesn't seem to always fire upon component mount (the more confusing thing is that sometimes it does, sometimes it doesn't)

I doubt

... network requests not fired? ... or your onCompleted conditions not met (no dispatch then)? No dispatch doesn't mean not requested/fired ...

onCompleted: (data) => {
    if (data !== undefined && data.getTaskTypes !== undefined && data.getTaskTypes !== state.taskTypes) {

Probably you can remove all conditions:

  • onCompleted is fired when data exists, no need to test it (data !== undefined)
  • getTaskTypes is nullable? If not (IMHO it shouldn't), don't test it, either;
  • data.getTaskTypes !== state.taskTypes - why don't ask API for this type only? Filtering not supported?

Your code is a bit confusing

useQuery(GET_TASK_TYPES(["taskId", "taskType"])

It looks like some generator/function (to define required fields?) - use const instead.

Redux

It looks like you are using redux to store received data. Apollo is much smarter (normalizing cache) and doesn't need to write custom actions for each data [type] fetch. You should use Apollo for that but probably you tested it and failed? ...

Missing id fields?

Your tasks doesn't have an id field but taskId? Apollo won't write it into cache. Luckily you can help Apollo to recognize unique identifiers in your data - https://www.apollographql.com/docs/react/caching/cache-configuration/#custom-identifiers

After this you can simply use data from useQuery for 1st render and refetch on demand/from event handler (without using useEffect hook).

6 Comments

hi @xadm, thanks for your reply! I have checked my onCompleted (it doesn't even fire outside the if statement). What do you mean by "ask API for this type"? I'm using typescript if that matters, so I need to check the type manually (can't seem to compile otherwise). Also not sure what you mean by "use const"? Lastly, about using data and refetch, how do I update the data variable with refetch? I have tested and data doesn't seem to update with the info from a new query. Thanks!
sorry forgot to say: I'm not using Redux, I'm using the Apollo Store. I have a Store.tsx defined with the dispatch resolvers etc.!
you should not check onCompleted firing but RAW/JSON network requests/response in browser dev tools .... ask API to return only some task type (if more task types) ... if it is a typescript then you should use typed useQuery to define response data type, not check it by code ... data should be changed, again, check nertwork response ... Apollo store? docs/example?
I have checked the Network in my browser as well, the backend data is correct there, and data doesn't match it
IMHO you should use { fetchPolicy: "network-only" } for this query
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.