Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useQuery and useLazyQuery should also return a promise #1486

Closed
ibex-david opened this issue Jun 29, 2023 · 4 comments · Fixed by Shoutzor/frontend#141 or Shoutzor/frontend#146
Closed
Assignees

Comments

@ibex-david
Copy link

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.

@Csszabi98
Copy link
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
}

@Akryum Akryum self-assigned this Jul 11, 2023
@sschneider-ihre-pvs
Copy link

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
Copy link
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
Copy link
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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment