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
)?
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)
"@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",
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.
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With