-
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
enable react-hooks
lint rules
#11511
Changes from all commits
2d5f02f
913ea1f
9e786aa
d757781
0e5e3cb
6ad1b0e
172f26c
c5a5d28
6070945
9c78b5c
8a33815
1aeb9ff
c34a48c
34ef7d8
c5bb497
8cdba15
2e751e1
d70b45e
0c813a1
e9c247b
619b188
7743e0b
694452f
9eaa883
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@apollo/client": patch | ||
--- | ||
|
||
`useLoadableQuery`: ensure that `loadQuery` is updated if the ApolloClient instance changes |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -148,6 +148,8 @@ | |
"eslint-import-resolver-typescript": "3.6.1", | ||
"eslint-plugin-import": "npm:@phryneas/[email protected]", | ||
"eslint-plugin-local-rules": "2.0.1", | ||
"eslint-plugin-react-compiler": "0.0.0-experimental-c8b3f72-20240517", | ||
"eslint-plugin-react-hooks": "4.6.2", | ||
"eslint-plugin-testing-library": "6.2.2", | ||
"expect-type": "0.19.0", | ||
"fetch-mock": "9.11.0", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
diff --git a/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js b/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js | ||
index 441442f..d1ec5dc 100644 | ||
--- a/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js | ||
+++ b/node_modules/eslint-plugin-react-hooks/cjs/eslint-plugin-react-hooks.development.js | ||
@@ -905,7 +905,7 @@ var ExhaustiveDeps = { | ||
var _callee = callee, | ||
name = _callee.name; | ||
|
||
- if (name === 'useRef' && id.type === 'Identifier') { | ||
+ if ((name === 'useRef' || name === "useLazyRef") && id.type === 'Identifier') { | ||
// useRef() return value is stable. | ||
return true; | ||
} else if (name === 'useState' || name === 'useReducer') { | ||
diff --git a/node_modules/eslint-plugin-react-hooks/index.js b/node_modules/eslint-plugin-react-hooks/index.js | ||
index 0e91baf..7e86d46 100644 | ||
--- a/node_modules/eslint-plugin-react-hooks/index.js | ||
+++ b/node_modules/eslint-plugin-react-hooks/index.js | ||
@@ -1,9 +1,3 @@ | ||
'use strict'; | ||
|
||
-// TODO: this doesn't make sense for an ESLint rule. | ||
-// We need to fix our build process to not create bundles for "raw" packages like this. | ||
-if (process.env.NODE_ENV === 'production') { | ||
- module.exports = require('./cjs/eslint-plugin-react-hooks.production.min.js'); | ||
-} else { | ||
- module.exports = require('./cjs/eslint-plugin-react-hooks.development.js'); | ||
-} | ||
+module.exports = require('./cjs/eslint-plugin-react-hooks.development.js'); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -245,12 +245,19 @@ export function useLoadableQuery< | |
|
||
setQueryRef(wrapQueryRef(queryRef)); | ||
}, | ||
[query, queryKey, suspenseCache, watchQueryOptions, calledDuringRender] | ||
[ | ||
query, | ||
queryKey, | ||
suspenseCache, | ||
watchQueryOptions, | ||
calledDuringRender, | ||
client, | ||
] | ||
); | ||
|
||
const reset: ResetFunction = React.useCallback(() => { | ||
setQueryRef(null); | ||
}, [queryRef]); | ||
}, []); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, I wasn't careful enough in my own code review when we released this to catch this. Let's definitely get this change in a patch 🙂 |
||
|
||
return [loadQuery, queryRef, { fetchMore, refetch, reset }]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -230,13 +230,16 @@ class InternalState<TData, TVariables extends OperationVariables> { | |
// initialization, this.renderPromises is usually undefined (unless SSR is | ||
// happening), but that's fine as long as it has been initialized that way, | ||
// rather than left uninitialized. | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might want to refactor this whole hook setup to not use a class anymore, it's not only confusing me to no end, it also confuses the lint rules. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes please |
||
this.renderPromises = React.useContext(getApolloContext()).renderPromises; | ||
|
||
this.useOptions(options); | ||
|
||
const obsQuery = this.useObservableQuery(); | ||
|
||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const result = useSyncExternalStore( | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
React.useCallback( | ||
(handleStoreChange) => { | ||
if (this.renderPromises) { | ||
|
@@ -307,7 +310,9 @@ class InternalState<TData, TVariables extends OperationVariables> { | |
// effectively passing this dependency array to that useEffect buried | ||
// inside useSyncExternalStore, as desired. | ||
obsQuery, | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
this.renderPromises, | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
this.client.disableNetworkFetches, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Am I missing anything here? |
||
] | ||
), | ||
|
@@ -533,6 +538,7 @@ class InternalState<TData, TVariables extends OperationVariables> { | |
this.observable || // Reuse this.observable if possible (and not SSR) | ||
this.client.watchQuery(this.getObsQueryOptions())); | ||
|
||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
this.obsQueryFields = React.useMemo( | ||
() => ({ | ||
refetch: obsQuery.refetch.bind(obsQuery), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,10 @@ export function useReadQuery<TData>( | |
// client that's available to us at the current position in the React tree | ||
// that ApolloClient will then have the job to recreate a real queryRef from | ||
// the transported object | ||
// This is just a context read - it's fine to do this conditionally. | ||
// This hook wrapper also shouldn't be optimized by React Compiler. | ||
// eslint-disable-next-line react-compiler/react-compiler | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
: useApolloClient() | ||
)(queryRef); | ||
} | ||
|
@@ -85,7 +89,7 @@ function _useReadQuery<TData>( | |
forceUpdate(); | ||
}); | ||
}, | ||
[internalQueryRef] | ||
[internalQueryRef, queryRef] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed |
||
), | ||
getPromise, | ||
getPromise | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -204,6 +204,8 @@ export function useSubscription< | |
} | ||
|
||
Object.assign(ref.current, { client, subscription, options }); | ||
// eslint-disable-next-line react-compiler/react-compiler | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [client, subscription, options, canResetObservableRef.current]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This might be a change in behaviour though - we need to be careful here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL we might have a tendency to overuse refs 😅 |
||
|
||
React.useEffect(() => { | ||
|
@@ -271,6 +273,8 @@ export function useSubscription< | |
subscription.unsubscribe(); | ||
}); | ||
}; | ||
// eslint-disable-next-line react-compiler/react-compiler | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [observable]); | ||
|
||
return result; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -251,18 +251,18 @@ function _useSuspenseQuery< | |
}, [queryRef.result]); | ||
|
||
const result = fetchPolicy === "standby" ? skipResult : __use(promise); | ||
const fetchMore = React.useCallback( | ||
((options) => { | ||
|
||
const fetchMore = React.useCallback< | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The lint rule had massive problems parsing the |
||
FetchMoreFunction<unknown, OperationVariables> | ||
>( | ||
(options) => { | ||
const promise = queryRef.fetchMore(options); | ||
setPromise([queryRef.key, queryRef.promise]); | ||
|
||
return promise; | ||
}) satisfies FetchMoreFunction< | ||
unknown, | ||
OperationVariables | ||
> as FetchMoreFunction<TData | undefined, TVariables>, | ||
}, | ||
[queryRef] | ||
); | ||
) as FetchMoreFunction<TData | undefined, TVariables>; | ||
|
||
const refetch: RefetchFunction<TData, TVariables> = React.useCallback( | ||
(variables) => { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,6 +81,8 @@ export const useSyncExternalStore: RealUseSESHookType = | |
// Force a re-render. | ||
forceUpdate({ inst }); | ||
} | ||
// React Hook React.useLayoutEffect has a missing dependency: 'inst'. Either include it or remove the dependency array. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tbh., I wouldn't touch our implementation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed. To be honest, I don't completely understand why we didn't just use the official polyfill to begin with, but it is what it is! Totally fine leaving this one as-is. |
||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [subscribe, value, getSnapshot]); | ||
} else { | ||
Object.assign(inst, { value, getSnapshot }); | ||
|
@@ -108,6 +110,8 @@ export const useSyncExternalStore: RealUseSESHookType = | |
forceUpdate({ inst }); | ||
} | ||
}); | ||
// React Hook React.useEffect has a missing dependency: 'inst'. Either include it or remove the dependency array. | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
}, [subscribe]); | ||
|
||
return value; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Up until now,
loadQuery
would not follow a newclient
instance.@jerelmiller this might be something we should still take into account for 3.9, or at least 3.9.1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call! I'm ok with doing a fast follow with 3.9.1 here if we want to keep with the code freeze. Thanks for finding!