apollo-client
apollo-client copied to clipboard
await useLazyQuery does not return a Promise on every call
Intended outcome: I'm trying to show an alert in react-native after awaiting a lazyQuery Promise. After every await i would expect to get a response which I can process. Either with data or at least with an error.
const [email, setEmail] = useState('');
const [resetPassword, { loading }] = useLazyQuery<
ResetPasswordData,
QueryResetPasswordArgs
>(RESET_PASSWORD_QUERY);
const handleError = useCallback(
(error: ApolloError) => {
if (error.message?.indexOf("Cannot find user") !== -1) {
Alert.alert("unknown user");
return;
}
Alert.alert("default error");
},
[]
);
const handleCompleted = useCallback(
(data: ResetPasswordData) => {
if (data.success) {
Alert.alert("password has been reset");
}
if (data.success === false) {
Alert.alert("default error");
}
},
[]
);
const onResetPassword = useCallback(
async (mail: string) => {
try {
console.log("sending request");
const response = await resetPassword({
variables: { email: mail },
});
console.log("response.error", response.error);
console.log("response.data", response.data);
if (response.error) {
handleError(response.error);
return;
}
if (response.data) {
handleCompleted(response.data);
return;
}
} catch (err) {
console.warn("error while resetting password", err);
Alert.alert("common.unknownError");
}
},
[resetPassword, handleCompleted, handleError]
);
<View>
<TextBox
onChangeText={setEmail}
value={email}
placeholder={t('common.email')}
/>
<Button
title={"reset password"}
loading={loading}
onPress={() => onResetPassword(email)}
/>
</View>
Actual outcome: When I change the textInput for a first time and i click the reset button, everything works as expected. If I click the button again with no textInput change, everything is still working as expected. Now if I change the textInput and click the button again, the console.log("sending"); appears and i can see the query on my server. So the query is really fired. But the promise does not resolve. I have to click the Button again to get another resolved Promise. So somehow one Promise never returns.
The console output is:
// click button
sending request
response.error [...]
response.data [...]
// click button
sending request
response.error [...]
response.data [...]
// changing text and click button
sending request
// click button
sending request
response.error [...]
response.data [...]
// changing text and click button
sending request
// changing text and click button
sending request
// changing text and click button
sending request
// click button
sending request
response.error [...]
response.data [...]
....
How to reproduce the issue: Create view with a textInput and a Button Fire the lazyQuery by pressing the button and passing the current value await the lazyQuery and process the response promise, which is not resolving every time.
Versions System: OS: macOS 11.6.2 Binaries: Node: 16.13.0 - ~/.nvm/versions/node/v16.13.0/bin/node Yarn: 1.22.17 - /usr/local/bin/yarn npm: 8.1.0 - ~/.nvm/versions/node/v16.13.0/bin/npm Browsers: Chrome: 97.0.4692.99 Firefox: 96.0.2 Safari: 15.2 npmPackages: @apollo/client: ^3.5.7 => 3.5.7 apollo-link-scalars: ^3.0.0 => 3.0.0 apollo-upload-client: ^16.0.0 => 16.0.0
@barlow21 Can you try 3.5.8? Might be fixed there.
With 3.5.8 the behaviour seems to be different but still not really as expected.
// changing text and click button
sending request
response.error undefined
response.data {"resetPassword": null}
// click button
sending request
error while resetting password // catch block
// click button
sending request
error while resetting password // catch block
// changing text and click button
sending request
response.error undefined
response.data {"resetPassword": null}
// click button
sending request
error while resetting password // catch block
So with the updated version an error is thrown on the second and subsequent requests when the text didn't change.
If I change the email input and send a first request neither an error is thrown nor is there a response.error object. Either I would expect the error to be thrown on every request or to have a response.error object on every request.
I can see my backend responding with the same output on every request ("cannot find user with..."), no matter if it was the first or n th request.
@brainkim 3.5.8 fixed this for me, thanks!
I'm still seeing incorrect results on the first call, as described by @barlow21's recent comment, in 3.5.9.
@defrex Could you try npm i @apollo/client@beta? The upcoming v3.6 release has a number of improvements/fixes for useLazyQuery.
We are experiencing the same issues in 3.6.6:
The Promise never returns and code after await lazyQuery(); is never reached although the Network tab clearly shows that the backend returned a valid response.
This issue is preventing us from upgrading to React 18, since we did not experience such issues on React 17.
We are experiencing the same issues in 3.6.6: The Promise never returns and code after
await lazyQuery();is never reached although the Network tab clearly shows that the backend returned a valid response. This issue is preventing us from upgrading to React 18, since we did not experience such issues on React 17.
Was it okay in React 17? I found out that executing useQuery did not return Promise, unless it is done by refetch
Was it okay in React 17?
Did a bit more testing on this and could also reproduce the buggy behavior of never returning (although network shows the correct result) with useLazyQuery in React 17 with apollo-client 3.6.9.
I found out that executing useQuery did not return Promise, unless it is done by refetch
Sorry, got no time to do own testing on useQuery and refetch at the moment
We are also experiencing the same behaviour @phebing-slashwhy described in React 17 with apollo-client 3.6.9
Is there any update on this?
Anyone still having issues with this? We were having issues as well where useLazyQuery does not always return a promise on every call
I am still experiencing this. awaiting a lazy query just hangs even after the network call has completed so no code after the await ever runs.
"react": "^18.2.0",
"@apollo/client": "^3.6.9",
More info: this seems to only happen for me when the component using the useLazyQuery hook is run on the server and then rehydrated on the client. When i navigate to the same component client side (as opposed to refreshing the page), the lazy query correctly returns a promise.
A less than ideal workaround I found is that onCompleted and onError are still called, so you can wrap the query in a promise and then it will work as expected.
const data = await new Promise((resolve, reject) =>
lazyQuery(QUERY, {
onCompleted: resolve,
onError: reject,
})
)
Also seeing this issue. Workaround from @brendanmorrell did it for me, but that is not ideal
- React 18.2
- NextJS 12.2
- Apollo 3.6.9
In case it helps, I'm also seeing this issue using Apollo client on React Native, with the following versions:
- React: 17.0.2
- React Native: 0.68.2
- @apollo/client: 3.6.8
The lazy query hangs, ~8 out of 10 times. onCompleted, and onError are still called correctly
More info: this seems to only happen for me when the component using the
useLazyQueryhook is run on the server and then rehydrated on the client. When i navigate to the same component client side (as opposed to refreshing the page), the lazy query correctly returns a promise.A less than ideal workaround I found is that
onCompletedandonErrorare still called, so you can wrap the query in a promise and then it will work as expected.const data = await new Promise((resolve, reject) => lazyQuery(QUERY, { onCompleted: resolve, onError: reject, }) )
Can confirm server side is not the case, we encounter this on client side in a React Native app too.
Encountered the same issue. Ended up using apollo client imperatively with useApolloClient.
We are also seeing this issue and workaround from https://github.com/apollographql/apollo-client/issues/9354#issuecomment-1234952933 works.
"@apollo/client": "3.7.2", "react": "18.2.0",
apollo: 3.7.0
react: 17.0.2
OnCompleted()/OnError() workaround https://github.com/apollographql/apollo-client/issues/9354#issuecomment-1234952933 works.
I can confirm the issue for client side react. Wrapping the lazy query in a promise did not work for me, so I queried the client directly (which seems a little less "hacky" to me anyway):
const client = useApolloClient();
....
const myQuery= await client.query({
query: MY_QUERY_CONST
variables: {...}
});
"@apollo/client": "3.7.5,
"react": "18.2.0"
Was facing this same issues with @apollo/client: "3.7.7" and react: "18.2.0". Turned out to be an issue with the way we were memoizing our ApolloClient instance that was being passed to <ApolloProvider />.
There were a variety of scenarios that caused us to create a brand new ApolloClient (like the user logging in and out). This issue was resolved by ensuring that we only ever create one instance of ApolloClient for the life of the app.
Instead of creating a new ApolloClient using the useMemo hook, we now just instantiate the ApolloClient at the top of the file and modify it as needed in useEffect hooks.
Thanks @NAllred91, we've got a similar issue. We were recreating the client every time we got a new token. Making sure to create only one instance of the client and setting the link property later by using useApolloClient solved the issue.
before
const client = new ApolloClient({
...
link: from([authLink, errorLink, httpLink]),
});
after
const client = useApolloClient();
client.setLink(from([authLink, errorLink, httpLink]));
Hey all 👋
Thanks for your patience on this issue! Reading through the original reported issue, I believe this may have been resolved in 3.7.4. 3.7.4 included a fix that would ensure promises don't get stuck in a pending state when the component unmounts. While I'm not sure unmounting is your problem here necessarily, these may be closely related. 3.7.11 did alter this behavior a little bit via https://github.com/apollographql/apollo-client/pull/10698 and made it so that the promise will resolve naturally rather than reject. You'll probably be happier with the change in 3.7.11, so please upgrade to this version to see if this fixes the issue. Thanks!
We're closing this issue now but feel free to ping the maintainers or open a new issue if you still need support. Thank you!
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. For general questions, we recommend using StackOverflow or our discord server.