jyjyj
jyjyj

Reputation: 55

How to render useQuery both on first render and onclick? [ReactJS, graphql]

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

Upvotes: 1

Views: 11725

Answers (2)

xadm
xadm

Reputation: 8418

, 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).

Upvotes: 0

Dlucidone
Dlucidone

Reputation: 1101

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>

Upvotes: 6

Related Questions