'How to trigger graphql useQuery on demand only once?

With the help of graphql-codegenerator we created a graphql useQuery that correctly retrieves data from the backend. When a string is entered into an input field the function driverRule is called to verify the input, which in turn should call the useQuery only once.

Problem

However, when we enable the query with the ref useQuery is immediately executed, a second execution is invoked with the refetch() method. All subsequent calls to driverRule will only invoke useQuery once as desired. But the very first call does it twice which is not desired.

import {computed, defineComponent, ref } from '@vue/composition-api'
import { useSapTruckRosterDriverQuery } from 'src/graphql/generated/operations'

export default defineComponent({
  setup(_, { emit }) {
    const showTruckId = ref(false)
    const driverId = computed(() => (!showTruckId.value ? answer.value : ''))
    const queryEnabled = ref<'driver' | 'truck'>()

    const { refetch: getDriverQuery } = useSapTruckRosterDriverQuery(
      () => ({ id: driverId.value }),
      () => ({ enabled: queryEnabled.value === 'driver' })
    )

    const driverRule = async (value: string) => {
      queryEnabled.value = 'driver'

      const result = await getDriverQuery({ id: value })

      if (result.data.driver.__typename === 'DriverArray') {
        return result.data.driver.data?.length ? true : 'Truck not found'
      }
    }

What we tried:

  • Move useQuery within the driverRule function but this yields the following error:

[Vue warn]: onServerPrefetch is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup().

  • useQuery with option emabled: false and only refetch() in driverRule. This does not execute useQuery at all

Workaround

Accept the default behavior and enable the query by default with a static variable value and not use the result from the first run but only the one from refetch(). This still results in 2 calls to the backend though, but it can't be avoided.

    const { refetch: getDriverQuery } = useSapTruckRosterDriverQuery(
      { id: 'initialQueryActivationCall' }
    )

    const driverRule = async (value: string) => {
      const result = await getDriverQuery({ id: value })
      if (result.data.driver.__typename === 'DriverArray') {
        return result.data.driver.data?.length ? true : 'Truck not found'
      }
    }

Question

It seems really hard to just call useQuery only once when the function driverRule is called. How can we avoid having 2 calls when only 1 call is needed to the backend?



Solution 1:[1]

You can use useApolloClient from @vue/apollo-composable package.

//other imports...
import { useApolloClient } from '@vue/apollo-composable'
import { gql } from '@apollo/client/core'

export default defineComponent({
    setup() {
        const { client } = useApolloClient()

        const GET_DOGS = gql`
            query GetDogs {
                dogs {
                   id
                   breed
                }
            }
        `;

        const getDogs = async (): Promise<void> => {
            const { data, } = await client.query({
                query: GET_DOGS
            })
        }

        return {
            getDogs,
        }
    },
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 JanuszO