apollo-client: useLazyQuery Uncaught (in promise)DOMException: signal is aborted without reason

Uncaught (in promise)DOMException: signal is aborted without reason

these below code maybe cause the exception,can we catch the exception?

abortControllersRef.current.forEach((controller) => {
       controller.abort();
     });
  asyncUpdate(signal: AbortSignal) {
    return new Promise<QueryResult<TData, TVariables>>((resolve, reject) => {
      const watchQueryOptions = this.watchQueryOptions;

      const handleAborted = () => {
        this.asyncResolveFns.delete(resolve)
        this.optionsToIgnoreOnce.delete(watchQueryOptions);
        signal.removeEventListener('abort', handleAborted)
        reject(signal.reason);
      };

      this.asyncResolveFns.add(resolve);
      this.optionsToIgnoreOnce.add(watchQueryOptions);
      signal.addEventListener('abort', handleAborted)
      this.forceUpdate();
    });
  }

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 17 (6 by maintainers)

Most upvoted comments

I’m also having an issue here, and don’t fully understand how this is marked resolved.

In my tests, using @apollo/client 3.7.9, and React 18.0.0, I get an Uncaught (in promise)DOMException: signal is aborted without reason error pretty much any time I use useLazyQuery in a component.

This seems to be because of React v18’s reactStrictMode, which causes components in dev mode to always unmount and re-render. See: https://github.com/vercel/next.js/issues/35822

So I don’t see how there’s anyway to use useLazyQuery and reactStrictMode: true together, without triggering this error all the time ?

A component that unmounts while a query is fetched truly is exceptional!

I’m not sure, seems to happen all the time with latest React v18 and reactStrictMode: true

Surely we don’t want to wrap everything in try/catch and check for error.name === 'AbortError' every time we use useLazyQuery ? It seems odd.

I know I could probably create my own version of useLazyQuery which by default ignores these errors (like @jerelmiller suggested here https://github.com/apollographql/apollo-client/pull/10427#issuecomment-1419679013)

So I’ll either do that, or downgraded to 3.7.3, as I don’t want to disable reactStrictMode.

But just trying to understand whether this really is intended behaviour or not for React 18 and reactStrictMode: true

Hello,

I have also the same issue after upgrading the @apollo/client library from version 3.7.1 to 3.7.8 when using useLazyQuery (probably the same with useQuery?).

The bug was introduced with version 3.7.4.

Here is the stack trace for reference:

useLazyQuery.ts:78 Uncaught (in promise) DOMException: signal is aborted without reason
    at http://localhost:3000/node_modules/.vite/deps/@apollo_client.js?v=d5c2e0d9:8702:20
    at Set.forEach (<anonymous>)
    at http://localhost:3000/node_modules/.vite/deps/@apollo_client.js?v=d5c2e0d9:8701:35
    at safelyCallDestroy (http://localhost:3000/node_modules/.vite/deps/chunk-JZ3YVIXN.js?v=8247418e:16737:13)
    at commitHookEffectListUnmount (http://localhost:3000/node_modules/.vite/deps/chunk-JZ3YVIXN.js?v=8247418e:16864:19)
    at invokePassiveEffectUnmountInDEV (http://localhost:3000/node_modules/.vite/deps/chunk-JZ3YVIXN.js?v=8247418e:18359:19)
    at invokeEffectsInDev (http://localhost:3000/node_modules/.vite/deps/chunk-JZ3YVIXN.js?v=8247418e:19697:19)
    at commitDoubleInvokeEffectsInDEV (http://localhost:3000/node_modules/.vite/deps/chunk-JZ3YVIXN.js?v=8247418e:19678:15)
    at flushPassiveEffectsImpl (http://localhost:3000/node_modules/.vite/deps/chunk-JZ3YVIXN.js?v=8247418e:19499:13)
    at flushPassiveEffects (http://localhost:3000/node_modules/.vite/deps/chunk-JZ3YVIXN.js?v=8247418e:19443:22)

@jerelmiller you have merged PR #10427 but it doesn’t seem to fix the issue (if it’s the same).

@70nyIT I’ve kept this closed because I’m going to track this in #10533 which is a pretty compelling case to change the behavior. I’d recommend following that issue to keep track of updates.

@guicara As mentioned in https://github.com/apollographql/apollo-client/issues/10520#issuecomment-1470354115, I’m planning on changing the behavior. We’ve got this issue queued up for us to look at, so it will get some attention in the near term.

Hey all 👋

We just released v3.7.11 that contains a change to the behavior of useLazyQuery that will now allow the promise to resolve naturally rather than rejecting with an abort error when a component unmounts. I hope this works better for you!

Confirmed working as expected! Thanks @jerelmiller

I’ve changed my mind. I’m going to reopen this issue so that I can tag it in my PR. This will close again when the PR is merged.

I agree with @guicara . This is why I was asking the reason for this issue being market as “Closed”

For me, it’s a bug AND a breaking change. A minor version upgrade should not cause something as important!

@jerelmiller in our project we use useLazyQuery most of the time. it allows us to control when to trigger the query or mutation. In the following example, we have a custom hook to fetch some data. The fetchUsers method is then called in a useEffect hook, or when a user clicks on a button (for example).

import { useLazyQuery } from '@apollo/client';
import { GET_USERS } from './query';

interface QueryData {
  users: User[];
}

interface QueryVars {
  sortBy: string;
  query: Pick<UserQueryInput, '_partition'>;
}

function useFetchUsers() {
  const [execute, { loading, error, data, refetch, called }] = useLazyQuery<QueryData, QueryVars>(
    GET_USERS,
    {
      notifyOnNetworkStatusChange: true,
    },
  );

  const fetchUsers = async (sortBy?: SortBy) => {
    const query: QueryVars = {
      query: { ... },
      sortBy,
    };

    const response = called
      ? await refetch(query)
      : await execute({
          variables: query,
        });

    return response.data?.users || [];
  };

  return { fetchUsers, loading, error, users: data?.users || [] };
}

export default useFetchUsers;
function Users() {
  const { fetchUsers, users, loading, error } = useFetchUsers();

  const [sortModel, setSortModel] = useState<GridSortModel>(defaultSortModel);

  const handleReload = () => {
    fetchUsers(sortModel[0] as SortBy);
  };

  useEffect(() => {
    if (!loading) {
      handleReload();
    }
  }, [users]);

  return (
      <Fragment>
        <button onClick={handleReload}>Reload</button>
        <UsersTable users={users} loading={loading} />
      </Fragment>
  );
}

export default Users;