Skip to content
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

fix: Fast Refresh/Live Reload should not break hooks #7952

Merged
merged 6 commits into from
Jul 15, 2021

Conversation

andreialecu
Copy link
Contributor

@andreialecu andreialecu commented Apr 6, 2021

Closes #5870

Supersedes #7186 which was not the correct fix for this issue. It would simply prevent the error, but would make the hook result unusable after a fast-refresh.

In particular, calling things like refetch() would stop working.

This PR is based on the comment by Dan Abramov in #5870 (comment)

I have verified it locally that it fixes the issue.

No tests currently, they'd be hard to add.

I put a patch-package-ready diff here: https://gist.github.com/andreialecu/49982426d4271773d9ff4935715fb2a6 for easy testing.

Checklist:

  • If this PR is a new feature, please reference an issue where a consensus about the design was reached (not necessary for small changes)
  • Make sure all of the significant new logic is covered by tests

@andreialecu andreialecu changed the title fix: fast refresh issue with hooks fix: Fast Refresh/Live Reload should not break hooks Apr 6, 2021
@andreialecu
Copy link
Contributor Author

andreialecu commented Apr 6, 2021

I believe useSubscription may have the same problem, as per:

useEffect(() => subscriptionData.cleanup.bind(subscriptionData), []);

If a core member wants to take over the PR and refactor this fix -> "Allow edits by maintainers" is on.

EDIT: I also applied the change to useSubscription in the latest commits.

@andreialecu
Copy link
Contributor Author

I refactored it into a separate hook.

This slightly bumps up the resulting file size, but it looks cleaner.

The patch-package diff of the latest changes:

diff --git a/node_modules/@apollo/client/react/hooks/hooks.cjs.js b/node_modules/@apollo/client/react/hooks/hooks.cjs.js
index a0acfe8..6ffb844 100644
--- a/node_modules/@apollo/client/react/hooks/hooks.cjs.js
+++ b/node_modules/@apollo/client/react/hooks/hooks.cjs.js
@@ -27,6 +27,23 @@ function useDeepMemo(memoFn, key) {
     return ref.current.value;
 }
 
+function useAfterFastRefresh(effectFn) {
+    if (__DEV__) {
+        var didRefresh_1 = React.useRef(false);
+        React.useEffect(function () {
+            return function () {
+                didRefresh_1.current = true;
+            };
+        }, []);
+        React.useEffect(function () {
+            if ((didRefresh_1 === null || didRefresh_1 === void 0 ? void 0 : didRefresh_1.current) === true) {
+                didRefresh_1.current = false;
+                effectFn();
+            }
+        }, []);
+    }
+}
+
 function useBaseQuery(query, options, lazy) {
     if (lazy === void 0) { lazy = false; }
     var context$1 = React.useContext(context.getApolloContext());
@@ -56,8 +73,14 @@ function useBaseQuery(query, options, lazy) {
     var queryResult = lazy
         ? result[1]
         : result;
+    if (__DEV__) {
+        useAfterFastRefresh(forceUpdate);
+    }
     React.useEffect(function () {
-        return function () { return queryData.cleanup(); };
+        return function () {
+            queryData.cleanup();
+            queryDataRef.current = undefined;
+        };
     }, []);
     React.useEffect(function () { return queryData.afterExecute({ lazy: lazy }); }, [
         queryResult.loading,
diff --git a/node_modules/@apollo/client/react/hooks/hooks.cjs.js.map b/node_modules/@apollo/client/react/hooks/hooks.cjs.js.map
index 57a0e46..23c5cec 100644
--- a/node_modules/@apollo/client/react/hooks/hooks.cjs.js.map
+++ b/node_modules/@apollo/client/react/hooks/hooks.cjs.js.map
@@ -1 +1 @@
-{"version":3,"file":"hooks.cjs.js","sources":["useApolloClient.js","utils/useDeepMemo.js","utils/useBaseQuery.js","useLazyQuery.js","useMutation.js","useQuery.js","useSubscription.js","useReactiveVar.js"],"sourcesContent":["import React from 'react';\nimport { invariant } from 'ts-invariant';\nimport { getApolloContext } from '../context';\nexport function useApolloClient() {\n    var client = React.useContext(getApolloContext()).client;\n    process.env.NODE_ENV === \"production\" ? invariant(client, 33) : invariant(client, 'No Apollo Client instance can be found. Please ensure that you ' +\n        'have called `ApolloProvider` higher up in your tree.');\n    return client;\n}\n//# sourceMappingURL=useApolloClient.js.map","import { useRef } from 'react';\nimport { equal } from '@wry/equality';\nexport function useDeepMemo(memoFn, key) {\n    var ref = useRef();\n    if (!ref.current || !equal(key, ref.current.key)) {\n        ref.current = { key: key, value: memoFn() };\n    }\n    return ref.current.value;\n}\n//# sourceMappingURL=useDeepMemo.js.map","import { __assign } from \"tslib\";\nimport { useContext, useEffect, useReducer, useRef } from 'react';\nimport { QueryData } from '../../data';\nimport { useDeepMemo } from './useDeepMemo';\nimport { getApolloContext } from '../../context';\nexport function useBaseQuery(query, options, lazy) {\n    if (lazy === void 0) { lazy = false; }\n    var context = useContext(getApolloContext());\n    var _a = useReducer(function (x) { return x + 1; }, 0), tick = _a[0], forceUpdate = _a[1];\n    var updatedOptions = options ? __assign(__assign({}, options), { query: query }) : { query: query };\n    var queryDataRef = useRef();\n    var queryData = queryDataRef.current || (queryDataRef.current = new QueryData({\n        options: updatedOptions,\n        context: context,\n        onNewData: function () {\n            if (!queryData.ssrInitiated()) {\n                Promise.resolve().then(function () { return queryDataRef.current && forceUpdate(); });\n            }\n            else {\n                forceUpdate();\n            }\n        }\n    }));\n    queryData.setOptions(updatedOptions);\n    queryData.context = context;\n    var memo = {\n        options: __assign(__assign({}, updatedOptions), { onError: undefined, onCompleted: undefined }),\n        context: context,\n        tick: tick\n    };\n    var result = useDeepMemo(function () { return (lazy ? queryData.executeLazy() : queryData.execute()); }, memo);\n    var queryResult = lazy\n        ? result[1]\n        : result;\n    useEffect(function () {\n        return function () { return queryData.cleanup(); };\n    }, []);\n    useEffect(function () { return queryData.afterExecute({ lazy: lazy }); }, [\n        queryResult.loading,\n        queryResult.networkStatus,\n        queryResult.error,\n        queryResult.data,\n    ]);\n    return result;\n}\n//# sourceMappingURL=useBaseQuery.js.map","import { useBaseQuery } from './utils/useBaseQuery';\nexport function useLazyQuery(query, options) {\n    return useBaseQuery(query, options, true);\n}\n//# sourceMappingURL=useLazyQuery.js.map","import { __assign } from \"tslib\";\nimport { useContext, useState, useRef, useEffect } from 'react';\nimport { MutationData } from '../data';\nimport { getApolloContext } from '../context';\nexport function useMutation(mutation, options) {\n    var context = useContext(getApolloContext());\n    var _a = useState({ called: false, loading: false }), result = _a[0], setResult = _a[1];\n    var updatedOptions = options ? __assign(__assign({}, options), { mutation: mutation }) : { mutation: mutation };\n    var mutationDataRef = useRef();\n    function getMutationDataRef() {\n        if (!mutationDataRef.current) {\n            mutationDataRef.current = new MutationData({\n                options: updatedOptions,\n                context: context,\n                result: result,\n                setResult: setResult\n            });\n        }\n        return mutationDataRef.current;\n    }\n    var mutationData = getMutationDataRef();\n    mutationData.setOptions(updatedOptions);\n    mutationData.context = context;\n    useEffect(function () { return mutationData.afterExecute(); });\n    return mutationData.execute(result);\n}\n//# sourceMappingURL=useMutation.js.map","import { useBaseQuery } from './utils/useBaseQuery';\nexport function useQuery(query, options) {\n    return useBaseQuery(query, options, false);\n}\n//# sourceMappingURL=useQuery.js.map","import { __assign } from \"tslib\";\nimport { useContext, useState, useRef, useEffect } from 'react';\nimport { SubscriptionData } from '../data';\nimport { getApolloContext } from '../context';\nexport function useSubscription(subscription, options) {\n    var context = useContext(getApolloContext());\n    var updatedOptions = options\n        ? __assign(__assign({}, options), { subscription: subscription }) : { subscription: subscription };\n    var _a = useState({\n        loading: !updatedOptions.skip,\n        error: undefined,\n        data: undefined\n    }), result = _a[0], setResult = _a[1];\n    var subscriptionDataRef = useRef();\n    function getSubscriptionDataRef() {\n        if (!subscriptionDataRef.current) {\n            subscriptionDataRef.current = new SubscriptionData({\n                options: updatedOptions,\n                context: context,\n                setResult: setResult\n            });\n        }\n        return subscriptionDataRef.current;\n    }\n    var subscriptionData = getSubscriptionDataRef();\n    subscriptionData.setOptions(updatedOptions, true);\n    subscriptionData.context = context;\n    useEffect(function () { return subscriptionData.afterExecute(); });\n    useEffect(function () { return subscriptionData.cleanup.bind(subscriptionData); }, []);\n    return subscriptionData.execute(result);\n}\n//# sourceMappingURL=useSubscription.js.map","import { useState, useEffect } from 'react';\nexport function useReactiveVar(rv) {\n    var value = rv();\n    var _a = useState(value), setValue = _a[1];\n    useEffect(function () { return rv.onNextChange(setValue); }, [value]);\n    useEffect(function () {\n        setValue(rv());\n    }, []);\n    return value;\n}\n//# sourceMappingURL=useReactiveVar.js.map"],"names":["React","getApolloContext","invariant","useRef","equal","context","useContext","useReducer","__assign","QueryData","useEffect","useState","MutationData","SubscriptionData"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"hooks.cjs.js","sources":["useApolloClient.js","utils/useDeepMemo.js","utils/useAfterFastRefresh.js","utils/useBaseQuery.js","useLazyQuery.js","useMutation.js","useQuery.js","useSubscription.js","useReactiveVar.js"],"sourcesContent":["import React from 'react';\nimport { invariant } from 'ts-invariant';\nimport { getApolloContext } from '../context';\nexport function useApolloClient() {\n    var client = React.useContext(getApolloContext()).client;\n    process.env.NODE_ENV === \"production\" ? invariant(client, 33) : invariant(client, 'No Apollo Client instance can be found. Please ensure that you ' +\n        'have called `ApolloProvider` higher up in your tree.');\n    return client;\n}\n//# sourceMappingURL=useApolloClient.js.map","import { useRef } from 'react';\nimport { equal } from '@wry/equality';\nexport function useDeepMemo(memoFn, key) {\n    var ref = useRef();\n    if (!ref.current || !equal(key, ref.current.key)) {\n        ref.current = { key: key, value: memoFn() };\n    }\n    return ref.current.value;\n}\n//# sourceMappingURL=useDeepMemo.js.map","import { useEffect, useRef } from \"react\";\nexport function useAfterFastRefresh(effectFn) {\n    if (__DEV__) {\n        var didRefresh_1 = useRef(false);\n        useEffect(function () {\n            return function () {\n                didRefresh_1.current = true;\n            };\n        }, []);\n        useEffect(function () {\n            if ((didRefresh_1 === null || didRefresh_1 === void 0 ? void 0 : didRefresh_1.current) === true) {\n                didRefresh_1.current = false;\n                effectFn();\n            }\n        }, []);\n    }\n}\n//# sourceMappingURL=useAfterFastRefresh.js.map","import { __assign } from \"tslib\";\nimport { useContext, useEffect, useReducer, useRef } from 'react';\nimport { QueryData } from '../../data';\nimport { useDeepMemo } from './useDeepMemo';\nimport { getApolloContext } from '../../context';\nimport { useAfterFastRefresh } from './useAfterFastRefresh';\nexport function useBaseQuery(query, options, lazy) {\n    if (lazy === void 0) { lazy = false; }\n    var context = useContext(getApolloContext());\n    var _a = useReducer(function (x) { return x + 1; }, 0), tick = _a[0], forceUpdate = _a[1];\n    var updatedOptions = options ? __assign(__assign({}, options), { query: query }) : { query: query };\n    var queryDataRef = useRef();\n    var queryData = queryDataRef.current || (queryDataRef.current = new QueryData({\n        options: updatedOptions,\n        context: context,\n        onNewData: function () {\n            if (!queryData.ssrInitiated()) {\n                Promise.resolve().then(function () { return queryDataRef.current && forceUpdate(); });\n            }\n            else {\n                forceUpdate();\n            }\n        }\n    }));\n    queryData.setOptions(updatedOptions);\n    queryData.context = context;\n    var memo = {\n        options: __assign(__assign({}, updatedOptions), { onError: undefined, onCompleted: undefined }),\n        context: context,\n        tick: tick\n    };\n    var result = useDeepMemo(function () { return (lazy ? queryData.executeLazy() : queryData.execute()); }, memo);\n    var queryResult = lazy\n        ? result[1]\n        : result;\n    if (__DEV__) {\n        useAfterFastRefresh(forceUpdate);\n    }\n    useEffect(function () {\n        return function () {\n            queryData.cleanup();\n            queryDataRef.current = undefined;\n        };\n    }, []);\n    useEffect(function () { return queryData.afterExecute({ lazy: lazy }); }, [\n        queryResult.loading,\n        queryResult.networkStatus,\n        queryResult.error,\n        queryResult.data,\n    ]);\n    return result;\n}\n//# sourceMappingURL=useBaseQuery.js.map","import { useBaseQuery } from './utils/useBaseQuery';\nexport function useLazyQuery(query, options) {\n    return useBaseQuery(query, options, true);\n}\n//# sourceMappingURL=useLazyQuery.js.map","import { __assign } from \"tslib\";\nimport { useContext, useState, useRef, useEffect } from 'react';\nimport { MutationData } from '../data';\nimport { getApolloContext } from '../context';\nexport function useMutation(mutation, options) {\n    var context = useContext(getApolloContext());\n    var _a = useState({ called: false, loading: false }), result = _a[0], setResult = _a[1];\n    var updatedOptions = options ? __assign(__assign({}, options), { mutation: mutation }) : { mutation: mutation };\n    var mutationDataRef = useRef();\n    function getMutationDataRef() {\n        if (!mutationDataRef.current) {\n            mutationDataRef.current = new MutationData({\n                options: updatedOptions,\n                context: context,\n                result: result,\n                setResult: setResult\n            });\n        }\n        return mutationDataRef.current;\n    }\n    var mutationData = getMutationDataRef();\n    mutationData.setOptions(updatedOptions);\n    mutationData.context = context;\n    useEffect(function () { return mutationData.afterExecute(); });\n    return mutationData.execute(result);\n}\n//# sourceMappingURL=useMutation.js.map","import { useBaseQuery } from './utils/useBaseQuery';\nexport function useQuery(query, options) {\n    return useBaseQuery(query, options, false);\n}\n//# sourceMappingURL=useQuery.js.map","import { __assign } from \"tslib\";\nimport { useContext, useState, useRef, useEffect } from 'react';\nimport { SubscriptionData } from '../data';\nimport { getApolloContext } from '../context';\nexport function useSubscription(subscription, options) {\n    var context = useContext(getApolloContext());\n    var updatedOptions = options\n        ? __assign(__assign({}, options), { subscription: subscription }) : { subscription: subscription };\n    var _a = useState({\n        loading: !updatedOptions.skip,\n        error: undefined,\n        data: undefined\n    }), result = _a[0], setResult = _a[1];\n    var subscriptionDataRef = useRef();\n    function getSubscriptionDataRef() {\n        if (!subscriptionDataRef.current) {\n            subscriptionDataRef.current = new SubscriptionData({\n                options: updatedOptions,\n                context: context,\n                setResult: setResult\n            });\n        }\n        return subscriptionDataRef.current;\n    }\n    var subscriptionData = getSubscriptionDataRef();\n    subscriptionData.setOptions(updatedOptions, true);\n    subscriptionData.context = context;\n    useEffect(function () { return subscriptionData.afterExecute(); });\n    useEffect(function () { return subscriptionData.cleanup.bind(subscriptionData); }, []);\n    return subscriptionData.execute(result);\n}\n//# sourceMappingURL=useSubscription.js.map","import { useState, useEffect } from 'react';\nexport function useReactiveVar(rv) {\n    var value = rv();\n    var _a = useState(value), setValue = _a[1];\n    useEffect(function () { return rv.onNextChange(setValue); }, [value]);\n    useEffect(function () {\n        setValue(rv());\n    }, []);\n    return value;\n}\n//# sourceMappingURL=useReactiveVar.js.map"],"names":["React","getApolloContext","invariant","useRef","equal","useEffect","context","useContext","useReducer","__assign","QueryData","useState","MutationData","SubscriptionData"],"mappings":"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"}
\ No newline at end of file
diff --git a/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.d.ts b/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.d.ts
new file mode 100644
index 0000000..d4e5605
--- /dev/null
+++ b/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.d.ts
@@ -0,0 +1,2 @@
+export declare function useAfterFastRefresh(effectFn: () => unknown): void;
+//# sourceMappingURL=useAfterFastRefresh.d.ts.map
\ No newline at end of file
diff --git a/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.d.ts.map b/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.d.ts.map
new file mode 100644
index 0000000..e14d5bb
--- /dev/null
+++ b/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"useAfterFastRefresh.d.ts","sourceRoot":"","sources":["../../../../src/react/hooks/utils/useAfterFastRefresh.ts"],"names":[],"mappings":"AAUA,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,OAAO,QAmB1D"}
\ No newline at end of file
diff --git a/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.js b/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.js
new file mode 100644
index 0000000..8441d9f
--- /dev/null
+++ b/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.js
@@ -0,0 +1,18 @@
+import { useEffect, useRef } from "react";
+export function useAfterFastRefresh(effectFn) {
+    if (__DEV__) {
+        var didRefresh_1 = useRef(false);
+        useEffect(function () {
+            return function () {
+                didRefresh_1.current = true;
+            };
+        }, []);
+        useEffect(function () {
+            if ((didRefresh_1 === null || didRefresh_1 === void 0 ? void 0 : didRefresh_1.current) === true) {
+                didRefresh_1.current = false;
+                effectFn();
+            }
+        }, []);
+    }
+}
+//# sourceMappingURL=useAfterFastRefresh.js.map
\ No newline at end of file
diff --git a/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.js.map b/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.js.map
new file mode 100644
index 0000000..e3897d1
--- /dev/null
+++ b/node_modules/@apollo/client/react/hooks/utils/useAfterFastRefresh.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"useAfterFastRefresh.js","sourceRoot":"","sources":["../../../../src/react/hooks/utils/useAfterFastRefresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAU1C,MAAM,UAAU,mBAAmB,CAAC,QAAuB;IAEzD,IAAI,OAAO,EAAE;QACX,IAAM,YAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACjC,SAAS,CAAC;YACR,OAAO;gBAEL,YAAU,CAAC,OAAO,GAAG,IAAI,CAAC;YAC5B,CAAC,CAAC;QACJ,CAAC,EAAE,EAAE,CAAC,CAAC;QAEP,SAAS,CAAC;YACR,IAAI,CAAA,YAAU,aAAV,YAAU,uBAAV,YAAU,CAAE,OAAO,MAAK,IAAI,EAAE;gBAEhC,YAAU,CAAC,OAAO,GAAG,KAAK,CAAC;gBAC3B,QAAQ,EAAE,CAAC;aACZ;QACH,CAAC,EAAE,EAAE,CAAC,CAAA;KACP;AACH,CAAC","sourcesContent":["import { useEffect, useRef } from \"react\";\n\n/**\n * This hook allow running a function only immediatelly after a react\n * fast refresh or live reload.\n *\n * Useful in order to ensure that we can reinitialize things that have been\n * disposed otherwise.\n * @param effectFn a function to run immediately after a fast refresh\n */\nexport function useAfterFastRefresh(effectFn: () => unknown) {\n  // @ts-expect-error: __DEV__ is a global exposed by react\n  if (__DEV__) {\n    const didRefresh = useRef(false);\n    useEffect(() => {\n      return () => {\n        // Detect fast refresh, only runs multiple times in fast refresh\n        didRefresh.current = true;\n      };\n    }, []);\n\n    useEffect(() => {\n      if (didRefresh?.current === true) {\n        // This block only runs after a fast refresh\n        didRefresh.current = false;\n        effectFn();\n      }\n    }, [])\n  }\n}\n"]}
\ No newline at end of file
diff --git a/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.d.ts.map b/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.d.ts.map
index bbb72c1..e3e1b6a 100644
--- a/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.d.ts.map
+++ b/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"useBaseQuery.d.ts","sourceRoot":"","sources":["../../../../src/react/hooks/utils/useBaseQuery.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,OAAO,EACL,gBAAgB,EAEhB,UAAU,EACV,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnD,wBAAgB,YAAY,CAAC,KAAK,GAAG,GAAG,EAAE,UAAU,GAAG,kBAAkB,EACvE,KAAK,EAAE,YAAY,GAAG,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,EAC1D,OAAO,CAAC,EAAE,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,EAC7C,IAAI,UAAQ,kEAkEb"}
\ No newline at end of file
+{"version":3,"file":"useBaseQuery.d.ts","sourceRoot":"","sources":["../../../../src/react/hooks/utils/useBaseQuery.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAEtE,OAAO,EACL,gBAAgB,EAEhB,UAAU,EACV,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAInD,wBAAgB,YAAY,CAAC,KAAK,GAAG,GAAG,EAAE,UAAU,GAAG,kBAAkB,EACvE,KAAK,EAAE,YAAY,GAAG,iBAAiB,CAAC,KAAK,EAAE,UAAU,CAAC,EAC1D,OAAO,CAAC,EAAE,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,EAC7C,IAAI,UAAQ,kEA6Eb"}
\ No newline at end of file
diff --git a/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js b/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js
index f98c293..fc51f72 100644
--- a/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js
+++ b/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js
@@ -3,6 +3,7 @@ import { useContext, useEffect, useReducer, useRef } from 'react';
 import { QueryData } from "../../data/index.js";
 import { useDeepMemo } from "./useDeepMemo.js";
 import { getApolloContext } from "../../context/index.js";
+import { useAfterFastRefresh } from "./useAfterFastRefresh.js";
 export function useBaseQuery(query, options, lazy) {
     if (lazy === void 0) { lazy = false; }
     var context = useContext(getApolloContext());
@@ -32,8 +33,14 @@ export function useBaseQuery(query, options, lazy) {
     var queryResult = lazy
         ? result[1]
         : result;
+    if (__DEV__) {
+        useAfterFastRefresh(forceUpdate);
+    }
     useEffect(function () {
-        return function () { return queryData.cleanup(); };
+        return function () {
+            queryData.cleanup();
+            queryDataRef.current = undefined;
+        };
     }, []);
     useEffect(function () { return queryData.afterExecute({ lazy: lazy }); }, [
         queryResult.loading,
diff --git a/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js.map b/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js.map
index 90efb29..4b3162a 100644
--- a/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js.map
+++ b/node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js.map
@@ -1 +1 @@
-{"version":3,"file":"useBaseQuery.js","sourceRoot":"","sources":["../../../../src/react/hooks/utils/useBaseQuery.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAUlE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,UAAU,YAAY,CAC1B,KAA0D,EAC1D,OAA6C,EAC7C,IAAY;IAAZ,qBAAA,EAAA,YAAY;IAEZ,IAAM,OAAO,GAAG,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACzC,IAAA,KAAsB,UAAU,CAAC,UAAA,CAAC,IAAI,OAAA,CAAC,GAAG,CAAC,EAAL,CAAK,EAAE,CAAC,CAAC,EAA9C,IAAI,QAAA,EAAE,WAAW,QAA6B,CAAC;IACtD,IAAM,cAAc,GAAG,OAAO,CAAC,CAAC,uBAAM,OAAO,KAAE,KAAK,OAAA,IAAG,CAAC,CAAC,EAAE,KAAK,OAAA,EAAE,CAAC;IAEnE,IAAM,YAAY,GAAG,MAAM,EAAgC,CAAC;IAC5D,IAAM,SAAS,GAAG,YAAY,CAAC,OAAO,IAAI,CACxC,YAAY,CAAC,OAAO,GAAG,IAAI,SAAS,CAAoB;QACtD,OAAO,EAAE,cAAqD;QAC9D,OAAO,SAAA;QACP,SAAS;YACP,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE;gBAO7B,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,cAAM,OAAA,YAAY,CAAC,OAAO,IAAI,WAAW,EAAE,EAArC,CAAqC,CAAC,CAAC;aACrE;iBAAM;gBAGL,WAAW,EAAE,CAAC;aACf;QACH,CAAC;KACF,CAAC,CACH,CAAC;IAEF,SAAS,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IACrC,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;IAK5B,IAAM,IAAI,GAAG;QACX,OAAO,EAAE,sBACJ,cAAc,KACjB,OAAO,EAAE,SAAS,EAClB,WAAW,EAAE,SAAS,GACgB;QACxC,OAAO,SAAA;QACP,IAAI,MAAA;KACL,CAAC;IAEF,IAAM,MAAM,GAAG,WAAW,CACxB,cAAM,OAAA,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,EAAtD,CAAsD,EAC5D,IAAI,CACL,CAAC;IAEF,IAAM,WAAW,GAAG,IAAI;QACtB,CAAC,CAAE,MAAwC,CAAC,CAAC,CAAC;QAC9C,CAAC,CAAE,MAAyC,CAAC;IAE/C,SAAS,CAAC;QACR,OAAO,cAAM,OAAA,SAAS,CAAC,OAAO,EAAE,EAAnB,CAAmB,CAAC;IACnC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,cAAM,OAAA,SAAS,CAAC,YAAY,CAAC,EAAE,IAAI,MAAA,EAAE,CAAC,EAAhC,CAAgC,EAAE;QAChD,WAAW,CAAC,OAAO;QACnB,WAAW,CAAC,aAAa;QACzB,WAAW,CAAC,KAAK;QACjB,WAAW,CAAC,IAAI;KACjB,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { useContext, useEffect, useReducer, useRef } from 'react';\nimport { DocumentNode } from 'graphql';\nimport { TypedDocumentNode } from '@graphql-typed-document-node/core';\n\nimport {\n  QueryHookOptions,\n  QueryDataOptions,\n  QueryTuple,\n  QueryResult,\n} from '../../types/types';\nimport { QueryData } from '../../data';\nimport { useDeepMemo } from './useDeepMemo';\nimport { OperationVariables } from '../../../core';\nimport { getApolloContext } from '../../context';\n\nexport function useBaseQuery<TData = any, TVariables = OperationVariables>(\n  query: DocumentNode | TypedDocumentNode<TData, TVariables>,\n  options?: QueryHookOptions<TData, TVariables>,\n  lazy = false\n) {\n  const context = useContext(getApolloContext());\n  const [tick, forceUpdate] = useReducer(x => x + 1, 0);\n  const updatedOptions = options ? { ...options, query } : { query };\n\n  const queryDataRef = useRef<QueryData<TData, TVariables>>();\n  const queryData = queryDataRef.current || (\n    queryDataRef.current = new QueryData<TData, TVariables>({\n      options: updatedOptions as QueryDataOptions<TData, TVariables>,\n      context,\n      onNewData() {\n        if (!queryData.ssrInitiated()) {\n          // When new data is received from the `QueryData` object, we want to\n          // force a re-render to make sure the new data is displayed. We can't\n          // force that re-render if we're already rendering however so to be\n          // safe we'll trigger the re-render in a microtask. In case the\n          // component gets unmounted before this callback fires, we re-check\n          // queryDataRef.current before calling forceUpdate().\n          Promise.resolve().then(() => queryDataRef.current && forceUpdate());\n        } else {\n          // If we're rendering on the server side we can force an update at\n          // any point.\n          forceUpdate();\n        }\n      }\n    })\n  );\n\n  queryData.setOptions(updatedOptions);\n  queryData.context = context;\n\n  // `onError` and `onCompleted` callback functions will not always have a\n  // stable identity, so we'll exclude them from the memoization key to\n  // prevent `afterExecute` from being triggered un-necessarily.\n  const memo = {\n    options: {\n      ...updatedOptions,\n      onError: undefined,\n      onCompleted: undefined\n    } as QueryHookOptions<TData, TVariables>,\n    context,\n    tick\n  };\n\n  const result = useDeepMemo(\n    () => (lazy ? queryData.executeLazy() : queryData.execute()),\n    memo\n  );\n\n  const queryResult = lazy\n    ? (result as QueryTuple<TData, TVariables>)[1]\n    : (result as QueryResult<TData, TVariables>);\n\n  useEffect(() => {\n    return () => queryData.cleanup();\n  }, []);\n\n  useEffect(() => queryData.afterExecute({ lazy }), [\n    queryResult.loading,\n    queryResult.networkStatus,\n    queryResult.error,\n    queryResult.data,\n  ]);\n\n  return result;\n}\n"]}
\ No newline at end of file
+{"version":3,"file":"useBaseQuery.js","sourceRoot":"","sources":["../../../../src/react/hooks/utils/useBaseQuery.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAUlE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,UAAU,YAAY,CAC1B,KAA0D,EAC1D,OAA6C,EAC7C,IAAY;IAAZ,qBAAA,EAAA,YAAY;IAEZ,IAAM,OAAO,GAAG,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACzC,IAAA,KAAsB,UAAU,CAAC,UAAA,CAAC,IAAI,OAAA,CAAC,GAAG,CAAC,EAAL,CAAK,EAAE,CAAC,CAAC,EAA9C,IAAI,QAAA,EAAE,WAAW,QAA6B,CAAC;IACtD,IAAM,cAAc,GAAG,OAAO,CAAC,CAAC,uBAAM,OAAO,KAAE,KAAK,OAAA,IAAG,CAAC,CAAC,EAAE,KAAK,OAAA,EAAE,CAAC;IAEnE,IAAM,YAAY,GAAG,MAAM,EAAgC,CAAC;IAC5D,IAAM,SAAS,GAAG,YAAY,CAAC,OAAO,IAAI,CACxC,YAAY,CAAC,OAAO,GAAG,IAAI,SAAS,CAAoB;QACtD,OAAO,EAAE,cAAqD;QAC9D,OAAO,SAAA;QACP,SAAS;YACP,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE;gBAO7B,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,cAAM,OAAA,YAAY,CAAC,OAAO,IAAI,WAAW,EAAE,EAArC,CAAqC,CAAC,CAAC;aACrE;iBAAM;gBAGL,WAAW,EAAE,CAAC;aACf;QACH,CAAC;KACF,CAAC,CACH,CAAC;IAEF,SAAS,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;IACrC,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;IAK5B,IAAM,IAAI,GAAG;QACX,OAAO,EAAE,sBACJ,cAAc,KACjB,OAAO,EAAE,SAAS,EAClB,WAAW,EAAE,SAAS,GACgB;QACxC,OAAO,SAAA;QACP,IAAI,MAAA;KACL,CAAC;IAEF,IAAM,MAAM,GAAG,WAAW,CACxB,cAAM,OAAA,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,EAAtD,CAAsD,EAC5D,IAAI,CACL,CAAC;IAEF,IAAM,WAAW,GAAG,IAAI;QACtB,CAAC,CAAE,MAAwC,CAAC,CAAC,CAAC;QAC9C,CAAC,CAAE,MAAyC,CAAC;IAG/C,IAAI,OAAO,EAAE;QAEX,mBAAmB,CAAC,WAAW,CAAC,CAAC;KAClC;IAED,SAAS,CAAC;QACR,OAAO;YACL,SAAS,CAAC,OAAO,EAAE,CAAC;YAGpB,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC;QACnC,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,cAAM,OAAA,SAAS,CAAC,YAAY,CAAC,EAAE,IAAI,MAAA,EAAE,CAAC,EAAhC,CAAgC,EAAE;QAChD,WAAW,CAAC,OAAO;QACnB,WAAW,CAAC,aAAa;QACzB,WAAW,CAAC,KAAK;QACjB,WAAW,CAAC,IAAI;KACjB,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { useContext, useEffect, useReducer, useRef } from 'react';\nimport { DocumentNode } from 'graphql';\nimport { TypedDocumentNode } from '@graphql-typed-document-node/core';\n\nimport {\n  QueryHookOptions,\n  QueryDataOptions,\n  QueryTuple,\n  QueryResult,\n} from '../../types/types';\nimport { QueryData } from '../../data';\nimport { useDeepMemo } from './useDeepMemo';\nimport { OperationVariables } from '../../../core';\nimport { getApolloContext } from '../../context';\nimport { useAfterFastRefresh } from './useAfterFastRefresh';\n\nexport function useBaseQuery<TData = any, TVariables = OperationVariables>(\n  query: DocumentNode | TypedDocumentNode<TData, TVariables>,\n  options?: QueryHookOptions<TData, TVariables>,\n  lazy = false\n) {\n  const context = useContext(getApolloContext());\n  const [tick, forceUpdate] = useReducer(x => x + 1, 0);\n  const updatedOptions = options ? { ...options, query } : { query };\n\n  const queryDataRef = useRef<QueryData<TData, TVariables>>();\n  const queryData = queryDataRef.current || (\n    queryDataRef.current = new QueryData<TData, TVariables>({\n      options: updatedOptions as QueryDataOptions<TData, TVariables>,\n      context,\n      onNewData() {\n        if (!queryData.ssrInitiated()) {\n          // When new data is received from the `QueryData` object, we want to\n          // force a re-render to make sure the new data is displayed. We can't\n          // force that re-render if we're already rendering however so to be\n          // safe we'll trigger the re-render in a microtask. In case the\n          // component gets unmounted before this callback fires, we re-check\n          // queryDataRef.current before calling forceUpdate().\n          Promise.resolve().then(() => queryDataRef.current && forceUpdate());\n        } else {\n          // If we're rendering on the server side we can force an update at\n          // any point.\n          forceUpdate();\n        }\n      }\n    })\n  );\n\n  queryData.setOptions(updatedOptions);\n  queryData.context = context;\n\n  // `onError` and `onCompleted` callback functions will not always have a\n  // stable identity, so we'll exclude them from the memoization key to\n  // prevent `afterExecute` from being triggered un-necessarily.\n  const memo = {\n    options: {\n      ...updatedOptions,\n      onError: undefined,\n      onCompleted: undefined\n    } as QueryHookOptions<TData, TVariables>,\n    context,\n    tick\n  };\n\n  const result = useDeepMemo(\n    () => (lazy ? queryData.executeLazy() : queryData.execute()),\n    memo\n  );\n\n  const queryResult = lazy\n    ? (result as QueryTuple<TData, TVariables>)[1]\n    : (result as QueryResult<TData, TVariables>);\n\n  // @ts-expect-error: __DEV__ is a global exposed by react\n  if (__DEV__) {\n    // ensure we run an update after refreshing so that we reinitialize\n    useAfterFastRefresh(forceUpdate);\n  }\n\n  useEffect(() => {\n    return () => {\n      queryData.cleanup();\n      // this effect can run multiple times during a fast-refresh\n      // so make sure we clean up the ref\n      queryDataRef.current = undefined;\n    }\n  }, []);\n\n  useEffect(() => queryData.afterExecute({ lazy }), [\n    queryResult.loading,\n    queryResult.networkStatus,\n    queryResult.error,\n    queryResult.data,\n  ]);\n\n  return result;\n}\n"]}
\ No newline at end of file

@andreialecu
Copy link
Contributor Author

@benjamn your comment in #7186 (review) mentioned reinitializing this.currentObservable.

The changes in this PR will do just that:

  1. by setting queryDataRef.current = undefined; in the cleanup function, and then forcing an update, it will simply be forced to reinitialize by this block:

const queryData = queryDataRef.current || (
queryDataRef.current = new QueryData<TData, TVariables>({
options: updatedOptions as QueryDataOptions<TData, TVariables>,
context,
onNewData() {
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. 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();
}
}
})
);

I believe this is the correct behavior. Currently it was left in a half-disposed state.

@gaearon
Copy link

gaearon commented Apr 6, 2021

FWIW, this is not so much a fix as a workaround. It would be better to find a proper fix. Is that possible? I'm not familiar with the Apollo architecture.

@andreialecu
Copy link
Contributor Author

andreialecu commented Apr 6, 2021

@gaearon I wonder what the proper fix would be then, considering that the current implementation uses a ref which it then disposes of in the cleanup function of an useEffect.

My initial attempt was to simply add queryDataRef.current = undefined; in the cleanup function - hoping that the hook would reinitialize everything itself on the next run.

The original code is rather simple:

Setup:

const queryDataRef = useRef<QueryData<TData, TVariables>>();
const queryData = queryDataRef.current || (
queryDataRef.current = new QueryData<TData, TVariables>({
options: updatedOptions as QueryDataOptions<TData, TVariables>,
context,
onNewData() {
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. 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();
}
}
})
);

Cleanup:

useEffect(() => {
return () => queryData.cleanup();
}, []);

The hook would not run again though, unless a forced update was issued, as per your comment in #5870 (comment) which used a similar implementation.

I'm a first time contributor to Apollo, so perhaps someone else would have more insight on whether a better solution is possible.

@gaearon
Copy link

gaearon commented Apr 6, 2021

I'm not sure I understand. The idea is that effects get cleaned up, and then re-run again. So as long as effect is 100% symmetrical, it should "just work".

Code like this is symmetrical:

useEffect(() => {
  let inst = new Foo()
  ref.current = inst
  inst.attach()
  return () => {
    inst.detach()
    ref.current = null
  }
}, [])

Code like this, of course, is not symmetrical:

useEffect(() => {
  let inst = ref.current
  return () => inst.detach()
}, [])

Can you make it symmetrical?

@andreialecu
Copy link
Contributor Author

The original code is pretty much like:

function useBaseQuery() {
  const queryDataRef = useRef();
  const queryData = queryDataRef.current || (queryDataRef.current = init());
  
  queryData.setOptions(updatedOptions);
  queryData.context = context;
  
  useEffect(() => {
    return () => queryData.cleanup();
  }, []);
}

I guess the initialization of queryData could be in an useEffect instead of in the body of the hook then. 🤔

@gaearon
Copy link

gaearon commented Apr 6, 2021

Yes, that would be the idiomatic solution that would also solve other future problems. The question is, why does Apollo do initialization during rendering. What does that initialization do?

@andreialecu
Copy link
Contributor Author

/cc @benjamn for that question 🙂

@benjamn
Copy link
Member

benjamn commented Apr 6, 2021

@gaearon We (are under the possibly mistaken impression that we) need to initialize the QueryData during rendering in order to have any opportunity to return useful data from our InMemoryCache the first time a component calls useQuery. Otherwise child components can't benefit from their parents having primed the cache, or the cache already having data in it thanks to local device persistence, etc. If we move the new QueryData bit into useEffect, that implies always returning { loading: true, data: undefined } the first time useQuery is called, no? I would love to be wrong about this, to be clear, but that's my understanding.

@gaearon
Copy link

gaearon commented Apr 6, 2021

Is it possible to create a 50 line isolated snippet illustrating what Apollo does, that doesn't actually use Apollo? E.g. a tiny object toy "cache" + an example of what useEffect does exactly that does the cache priming.

@benjamn
Copy link
Member

benjamn commented Apr 6, 2021

I'll stick to prose because I don't want toy code to mislead you or anyone else who finds this issue in the future. I very much invite follow-up questions to make the explanation as accurate as it needs to be.

Internally, useQuery calls client.watchQuery, which returns an ObservableQuery (which is an Observable) that's responsible for delivering any/all query results, whether they come from the cache or from the network. Although this ObservableQuery is capable of delivering its first result synchronously, it also has methods like getCurrentResult that will read directly (and synchronously) from the client.cache, if need be (ObservableQuery stores the most recent result after it starts receiving results, but may need to read from the cache if it hasn't received any results yet).

The full query request process is initiated the first time an observer subscribes to that ObservableQuery, so we potentially have some room to delay the effectful network request, while still creating the ObservableQuery synchronously so that we can use its API to obtain existing cache results.

@gaearon Are you perhaps suggesting that we should wait to subscribe until the first useEffect fires, but still call client.watchQuery synchronously in render (within useQuery), so we can use the ObservableQuery API to read from the cache? Could that be an idiomatic solution to this problem, or is any kind of stateful initialization in render a no-go?

@gaearon
Copy link

gaearon commented Apr 6, 2021

So rendering by itself should not add subscriptions, since it doesn't necessarily guarantee it will show up on the screen. Today this is the case for errors during rendering (they have no cleanup phase), but in the longer term more React features will work like this — it's assumed that it's safe to run a render but then throw away the result. So if you subscribe in render, you'll have a memory leak. Priming a cache is okay though. Reading the current value, with some caveats, is also okay — we can talk about this later in more depth. But for now it would make sense to me if you delayed any subscriptions until first effect, and only used rendering to prime the cache and/or get the initial value from the cache.

@capaj
Copy link
Contributor

capaj commented Apr 19, 2021

@andreialecu thank you so much for that patch package. Just applied it on our big FE app at @LeapLabs and it works like a charm, even with vite.js and their react-fast-refresh plugin.
It's a webapp so I had to define the __DEV__ global first obviously.

@andreialecu
Copy link
Contributor Author

@benjamn could you share an update on whether you're planning a better fix for this, as per #7952 (comment) ?

I also noticed #6037 is related to this behavior.

@capaj
Copy link
Contributor

capaj commented Apr 19, 2021

you're planning a better fix for this

IMHO unless that better fix can be done in matter of days, as an apollo client user I would humbly ask the maintainers for this to get merged as interim patch and the better fix can come later with better performance on top of this.

@benjamn benjamn added this to the Release 3.4 milestone Apr 19, 2021
@hwillson hwillson added this to Planned in Release 3.4 Apr 21, 2021
@hwillson hwillson removed this from Planned in Release 3.4 Apr 29, 2021
@jcreighton jcreighton self-assigned this Apr 30, 2021
@hwillson hwillson modified the milestones: June 2021, July 2021 Jun 15, 2021
@capaj
Copy link
Contributor

capaj commented Jul 14, 2021

Just reporting that we have been using this patchfile in development for over 3 months now with apollo-client 3.3.15 on a big FE app(around 150k LOC).
We haven't seen anything broken in apollo-client in that time-not anything related to this change anyway.

benjamn added a commit to andreialecu/apollo-client that referenced this pull request Jul 15, 2021
Copy link
Member

@benjamn benjamn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Echoing comments above, I'm happy with this solution as a stop-gap until we can adequately revisit/rewrite this area of the codebase.

Although it would be great to have some sort of regression test for this behavior, the useAfterFastRefresh code is heavily exercised by our existing test suite, without any apparent problems.

@brainkim Shall we merge this into release-3.4 so we can release it for testing in the next RC?

@brainkim
Copy link
Contributor

doit

@benjamn
Copy link
Member

benjamn commented Jul 15, 2021

These changes should now be available in @apollo/client@3.4.0-rc.20. Thanks @andreialecu for getting this started, and to everyone else for the valuable discussion!

@brainkim
Copy link
Contributor

@andreialecu This PR had no tests huh.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Fast-refresh issue
8 participants