Skip to content

useQuery and useLazyQuery should also return a promise #1486

Closed
@ibex-david

Description

@ibex-david

The problem is that i still want to wait for a specific request made by the lazy query.

// at my store:
const { load, loading, error, result } = useLazyQuery()

// inside the component:
const data = await load()

Because load doesn't return the current request promise, many reactive variables needed to be watched unwillingly.

The suggested solution

load() should return the promise to the current request.
also every other fetching function like rerun etc.

Activity

Csszabi98

Csszabi98 commented on Jul 6, 2023

@Csszabi98
Contributor

For now you can do:

function waitApolloQuery({ onResult, onError }) {
  return new Promise((res, rej) => {
    const { off: offResult } = onResult((result) => {
      offResult()
      res(result)
    })
    const { off: offError } = onError((error) => {
      offError()
      rej(error)
    })
  })
}

const query = useLazyQuery()

try {
  query.load()
  const data = await waitApolloQuery(query)
} catch(e) {
  // Handle error
}
self-assigned this
on Jul 11, 2023
sschneider-ihre-pvs

sschneider-ihre-pvs commented on Aug 25, 2023

@sschneider-ihre-pvs

For now you can do:

function waitApolloQuery({ onResult, onError }) {
  return new Promise((res, rej) => {
    const { off: offResult } = onResult((result) => {
      offResult()
      res(result)
    })
    const { off: offError } = onError((error) => {
      offError()
      rej(error)
    })
  })
}

const query = useLazyQuery()

try {
  query.load()
  const data = await waitApolloQuery(query)
} catch(e) {
  // Handle error
}

{loading: true, networkStatus: 1, partial: true} doesn't seem to work

tcitworld

tcitworld commented on Aug 31, 2023

@tcitworld
Contributor

You can make sure the promise is only resolved when the query is no longer loading.

const { off: offResult } = onResult((result) => {
+     if (result.loading === false) {
        offResult()
        res(result)
+     }
    })
Csszabi98

Csszabi98 commented on Sep 4, 2023

@Csszabi98
Contributor

@sschneider-ihre-pvs That is a different issue, useLazyQuery does not invoke onServerPrefetch in this case. See: #1495. You can use the mentioned workaround to fix that.

Also, this is the final implementation of waitForApolloQuery that ended up working for me:

import { ApolloError, OperationVariables } from '@apollo/client';
import { UseQueryReturn } from '@vue/apollo-composable';

export function waitForApolloQuery<TResult, TVariables extends OperationVariables>({
  onResult,
  onError,
  forceDisabled,
}: Pick<UseQueryReturn<TResult, TVariables>, 'onResult' | 'onError' | 'forceDisabled'>) {
  if (forceDisabled.value) {
    return Promise.resolve<TResult | ApolloError | undefined>(undefined);
  }

  return new Promise<TResult | ApolloError | undefined>((res) => {
    const { off: offResult } = onResult((result) => {
      res(result.data);
      setTimeout(offResult, 1);
    });
    const { off: offError } = onError((error) => {
      res(error);
      setTimeout(offError, 1);
    });
  });
}

Usage of setTimeout is required if you have multiple async functions waiting for the same Apollo query. (onResult and onError are invoked inside of a for cycle and synchronous removal of the event handlers will affect their invocation order, leading to not all handlers being called)

added a commit that references this issue on Mar 26, 2024
96cc4fe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
`useQuery` and `useLazyQuery` should also return a promise · Issue #1486 · vuejs/apollo