-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Heads up: Deprecation of useQuery
and useLazyQuery
lifecycle hooks
#12352
Comments
Is a global error handling approach feasible for large apps? I think in practice this will push people towards adopting suspense + error boundaries |
@dacevedo12 global error handling can be achieved through the error link already. Are you looking for something other than this? |
I'm concerned about the feasibility (and maintainability) of having a single giant error link handling all possible errors in a large app. The same error can mean different things or be expected to have different handling depending on the view it's on I see value in a localized onError |
@dacevedo12 I'm curious, what would |
Deprecation of
useQuery
anduseLazyQuery
lifecycle hooksWith the release of Apollo Client 3.13, we will be deprecating the
useQuery
anduseLazyQuery
lifecycle hooksonCompleted
andonError
and will be removing them in Apollo Client 4.0.These lifecycle hooks have long been the cause of confusion, bugs and frustration for many developers. With this step we are following other tools like React Query in their removal.
We encourage you to read this comprehensive blog post by React Query's maintainer Dominik Dorfmeister which provides some great insight into the pitfalls of these callback APIs. Apollo Client shares many of the same concerns.
While Apollo Client shares similar concerns, there are some additional reasons behind this change.
onCompleted
Conflicting interpretations for
onCompleted
behaviorApollo Client uses a normalized cache, which means that there are many different reasons why the data displayed by a component might change:
fetchMore
,refetch
, etc.Apollo Client users have provided logical justification for each of these cases for why the
onCompleted
callback should or should not execute.Added ambiguity around
@defer
With the introduction of the
@defer
directive in the GraphQL ecosystem, we have yet another source of "updates" which provides further complication.Should
onCompleted
run once the initial chunk of data arrives? After all deferred fragments arrived? After each fragment?While one behavior might make sense to some, others might have vastly different conflicting opinions that are equally valid.
Changes around the behaviour
Adding insult to injury,
onCompleted
had a bug in versions from versions 3.5 to 3.7 where cache changes in addition to network requests would execute theonCompleted
callback. This was fixed in version 3.8 with #10229, but the damage was done.While the users who initially reported the bug were satisfied, others came to expect the behavior from 3.5 to 3.7 as the correct behavior.
Given this history, we are not confident that we can provide an approach that is intuitive for everyone and doesn't add more confusion among our userbase.
Our recommendation
As we've received questions about
onCompleted
through various issues and our community forum, the solutions we propose often involve moving away fromonCompleted
entirely. Many of the cases where we seeonCompleted
usedinvolve some kind of state syncing which is a highly discouraged pattern (see the section on "State
Syncing" in Dominik's blog post for an example.) We have recommended against using the the use of these callbacks for quite some time now for one reason or another and believe its time for us to sunset these APIs.
Bugs
The final straw that made us come to this decision was this bug report:
With the current implementation, in some circumstances, the
onCompleted
andonError
callbacks can be stale by one render. Unfortunately there is no perfect solution that can prevent that from happening in a manner that won't introduce new bugs, for example when using suspense in your App.React's
useEffectEvent
hook might solve this problem for us, but that hook is still experimental.Even when it is available, it won't be backported to the old React versions which means we cannot provide a working solution for our entire userbase.
This isn't the first issue that's been opened in regards to timing issues with
onCompleted
. Once again, this is one of those cases where varying logical opinions make it impossible to determine the correct behavior. React itself doesnot guarantee stability on the timing of renders between major versions which further complicates this issue as upgrading React versions might subtly change the timing of when
onCompleted
executes.With the current available primitives, fixing this might be possible in a very hacky way, but given everything else and the fact that we discourage its use, we want to move everybody off these callbacks instead of pushing additional bundle
size on all our users.
What to use instead
Once again, we recommend reading the blog article by Dominik Dorfmeister which provides a lot of answers on what to use in place of these callbacks.
In short:
useMemo
key
setState
function ofuseState
during component render, so you can use this to compare new results with old results and modify state as a consequence.See this example in the React docs.
Keep in mind that this is a very rare use case and you should usually go with
useMemo
.networkStatus
useEffect
.onError
onError
is too localizedThe
onError
callback will only execute when a query executed by the current hook returns an error.While that seems fine, remember: if you have two components calling the
useQuery
hook with the same query, but differentonError
callbacks, only one or the other will execute - depending on the order the components were rendered in, and also influenced by prior cache contents.Bugs
onError
suffers from the same potential timing issue described foronCompleted
.What to use instead
Your component is probably the wrong place to handle errors like this, and you should probably do it in a more centralized place such as an
onError
link, or if you are using the suspenseful hooks, in anErrorBoundary
that is parent toall components potentially calling your query. Future versions of the client will switch to throwing errors by default which further negates the need for the
onError
callback.useMutation
is not affected by this deprecationuseMutation
doesn't suffer from the same problems laid out here so these callbacks are unaffected. While we can't guarantee that we won't deprecate these callbacks in a future version, this deprecation is focused onuseQuery
anduseLazyQuery
.The text was updated successfully, but these errors were encountered: