Skip argument is being ignored in useQuery hook from @apollo/react-hooks
Asked Answered
F

2

6

The issue:

Hello, so I've been using apollo-client for a while on my ReactJS application. I've just noticed that sometimes when I use the useQuery hook, the execution completely ignores the skip argument, and just proceed with the onCompleted (albeit without any data). Interestingly enough, it also does not make an API request to my endpoint either. However, if I just set skip to false, then everything works properly (as expected).

Also, this doesn't happen every time I use useQuery with skip, it seems working on some and not others.

Why is apollo ignoring the skip argument and just executing onCompleted immediately with null data? Is there a fix (other than useLazyQuery)?

Code example:

const userQuery = useQuery(GET_USER, {
    variables: {
      value: userId,
    },
    skip: true,
    onCompleted: props => {
      console.log(`props => `, props)
      const {
        user: { type, role, firstName, lastName, permissions },
      } = props;

      setValue('firstName', firstName);
      setValue('lastName', lastName);
      setValue(
        'type',
        userTypes.find(({ value }) => value === type),
      );
      setValue(
        'role',
        userRoles.find(({ value }) => value === role),
      );
    },
  });

Additional notes:

• The output of logging the props from the onCompleted function is props => undefined.

• I added skip: true just to demonstrate the fact that it actually isn't working.

• If I log userQuery itself, then immediately on the first log, userQuery.called is equal to true (but like I said previously, no API call actually had been executed)

Dependencies

"@apollo/react-hooks": "^3.1.5",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.10",
"apollo-datasource-rest": "^0.6.6",
"apollo-link": "^1.2.13",
"apollo-link-context": "^1.0.19",
"apollo-link-error": "^1.1.12",
"apollo-link-rest": "^0.7.3",
"apollo-link-retry": "^2.2.15",
"apollo-link-token-refresh": "^0.2.7",
"apollo-upload-client": "^11.0.0",
"react": "16.10.2",
"react-apollo": "^3.1.3",

Note:

useLazyQuery seems to be working properly, so as a workaround for this in the meantime you can use that in combination with useEffect to achieve similar results.

Ferrate answered 20/5, 2020 at 12:7 Comment(1)
We also encountered this issue, and others too: github.com/apollographql/apollo-client/issues/6190Rhizocarpous
S
3

Apollo Client has issues. The bug in skip is an annoying one. You can abandon GraphQL, or work around them. I recommend abandoning it, but if you can't here is a work around. This module will work similar to useQuery and have skip working. It is incomplete to the full spec, but should get you started.

import { useEffect, useState } from 'react';
import { QueryHookOptions, useApolloClient } from '@apollo/react-hooks';
import { ApolloQueryResult, NetworkStatus, QueryOptions, ApolloError } from 'apollo-client';

interface DocumentNode {
    loc: {
        source: {
            body: string;
        };
    };
}

interface QueryResult<T> extends ApolloQueryResult<T> {
    error?: Error | ApolloError;
    refetch?: () => void;
}

/**
 * Method: useQuery
 * Description: enable skip on useQuery. There is a bug that currently ignores it. The bug is confirmed in the 3.2 release as well.
 * Note: https://github.com/apollographql/react-apollo/issues/3492
 */
export function useQuery<T>(
    query: DocumentNode,
    options?: QueryHookOptions
): QueryResult<T | null> {
    // Note: using useApolloClient because useLazyQuery fires on initialization. If we are skipping, we don't even want the first fire.
    const apolloClient = useApolloClient();
    const [result, setQueryResult] = useState<QueryResult<T | null>>({
    // Note: initial state
        data: null,
        loading: false,
        stale: false,
        networkStatus: NetworkStatus.ready
    });

    const setResult = (res: QueryResult<T | null>) => {
    // Note: GraphQL and Apollo can't decide if they want to return errors, or error in the type contract. I want to be sure the contract is stable. If there are errors, there will be error.
    if (res.errors?.length && !res.error) {
        [res.error] = res.errors;
    }
    // Note: Functions should always exist even if the query is not complete.
    if (!res.refetch) {
        res.refetch = () => {
            if (!result.loading) {
                execQuery();
            }
        };
      }
      setQueryResult(res);
  };

  const execQuery = async () => {
      // Note: loading state. Not spreading "...result" to allow errors to be reset.
      setResult({
          data: options?.fetchPolicy === 'no-cache' ? null : result.data,
          loading: true,
          networkStatus: NetworkStatus.loading,
          stale: true
      });
      try {
          const queryOptions = ({
              ...options,
              query
          } as unknown) as QueryOptions;
          
          await apolloClient
              .query(queryOptions)
              .then(result => {
                  setResult(result);
                  if(options?.onCompleted) options.onCompleted(result.data);
              })
              .catch(err => {
                  setResult(err);
                  if(options?.onError) options.onError(err);
              });
          
      } catch (err) {
          // Note: fail state
          setResult({
              ...result,
              loading: false,
              errors: [err],
              error: err,
              stale: true,
              networkStatus: NetworkStatus.error
          });
      }
  };

  useEffect(() => {
      // Note: Skip Functionality
      if (!result.loading && options?.skip !== true) {
           execQuery();
      }
  // Note only listen too explicit variables. If you listen to whole objects they are always different and will cause endless loops.
  }, [options?.skip, query.loc.source.body]);

  return result;
}
Slavocracy answered 17/2, 2021 at 0:28 Comment(0)
L
0

Just use enabled: false - that worked for me.

Lynden answered 13/3, 2023 at 8:56 Comment(3)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Bongbongo
enabled: false is for react-query. Not Apollo!Objectivism
The enabled argument can be used for ApolloLynden

© 2022 - 2024 — McMap. All rights reserved.