diff --git a/CHANGELOG.md b/CHANGELOG.md
index bbb242a5624..54edeff41be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## Apollo Client 3.3.11
+
+### Bug fixes
+
+- Fix `useLazyQuery` `forceUpdate` loop regression introduced by [#7655](https://github.com/apollographql/apollo-client/pull/7655) in version 3.3.10.
+ [@benjamn](https://github.com/benjamn) in [#7715](https://github.com/apollographql/apollo-client/pull/7715)
+
## Apollo Client 3.3.10
### Bug fixes
diff --git a/src/react/hooks/utils/useBaseQuery.ts b/src/react/hooks/utils/useBaseQuery.ts
index 30823c25698..7052df65fc0 100644
--- a/src/react/hooks/utils/useBaseQuery.ts
+++ b/src/react/hooks/utils/useBaseQuery.ts
@@ -23,36 +23,31 @@ export function useBaseQuery(
const updatedOptions = options ? { ...options, query } : { query };
const queryDataRef = useRef>();
- const queryData =
- queryDataRef.current ||
- new QueryData({
+ const queryData = queryDataRef.current || (
+ queryDataRef.current = new QueryData({
options: updatedOptions as QueryDataOptions,
context,
onNewData() {
- if (!queryData.ssrInitiated() && queryDataRef.current) {
+ if (!queryData.ssrInitiated()) {
// When new data is received from the `QueryData` object, we want to
// force a re-render to make sure the new data is displayed. We can't
// force that re-render if we're already rendering however so to be
- // safe we'll trigger the re-render in a microtask.
- Promise.resolve().then(forceUpdate);
+ // safe we'll trigger the re-render in a microtask. In case the
+ // component gets unmounted before this callback fires, we re-check
+ // queryDataRef.current before calling forceUpdate().
+ Promise.resolve().then(() => queryDataRef.current && forceUpdate());
} else {
// If we're rendering on the server side we can force an update at
// any point.
forceUpdate();
}
}
- });
+ })
+ );
queryData.setOptions(updatedOptions);
queryData.context = context;
- // SSR won't trigger the effect hook below that stores the current
- // `QueryData` instance for future renders, so we'll handle that here if
- // the current render is happening on the server side.
- if (queryData.ssrInitiated() && !queryDataRef.current) {
- queryDataRef.current = queryData;
- }
-
// `onError` and `onCompleted` callback functions will not always have a
// stable identity, so we'll exclude them from the memoization key to
// prevent `afterExecute` from being triggered un-necessarily.
@@ -76,12 +71,6 @@ export function useBaseQuery(
: (result as QueryResult);
useEffect(() => {
- // We only need one instance of the `QueryData` class, so we'll store it
- // as a ref to make it available on subsequent renders.
- if (!queryDataRef.current) {
- queryDataRef.current = queryData;
- }
-
return () => queryData.cleanup();
}, []);