Fix external auth buttons in React Strict Mode#6175
Conversation
🦋 Changeset detectedLatest commit: 59b0b21 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
Pull request overview
This PR addresses issues with external authentication buttons in React Strict Mode by converting from a lazy query to a regular query with skip logic and implementing a ref-based guard to prevent double-processing of authentication callbacks.
Key Changes:
- Replaced
useAvailableExternalAuthenticationsLazyQuerywithuseAvailableExternalAuthenticationsQueryusing conditional skip logic - Added
useRefto prevent double-processing of external auth callbacks in React Strict Mode - Removed problematic cleanup logic from useEffect that was clearing localStorage during simulated unmounts
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/auth/views/Login.tsx | Converted lazy query to regular query with skip parameter; added ref-based guard for callback processing; removed cleanup logic from useEffect |
| src/auth/components/LoginPage/LoginPage.test.tsx | Improved test structure with helper function; added test cases for edge cases (empty array, undefined); removed mock in favor of actual MemoryRouter |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6175 +/- ##
========================================
Coverage 39.56% 39.56%
========================================
Files 2415 2415
Lines 39920 39918 -2
Branches 8821 9149 +328
========================================
+ Hits 15794 15795 +1
+ Misses 24100 24097 -3
Partials 26 26 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/auth/views/Login.tsx
Outdated
| useEffect(() => { | ||
| const { code, state } = params; | ||
| const externalAuthParamsExist = code && state && isCallbackPath; | ||
| const externalAuthNotPerformed = !authenticating && !errors.length; | ||
|
|
||
| if (externalAuthParamsExist && externalAuthNotPerformed) { | ||
| handleExternalAuthentication(code, state); | ||
| // Guard against double-processing in React Strict Mode | ||
| if (isExternalAuthCallback && externalAuthNotPerformed && !externalAuthProcessedRef.current) { | ||
| externalAuthProcessedRef.current = true; | ||
| handleExternalAuthentication(code!, state!); | ||
| } | ||
|
|
||
| return () => { | ||
| setRequestedExternalPluginId(null); | ||
| setFallbackUri(null); | ||
| }; | ||
| }, []); | ||
| }, [isExternalAuthCallback, authenticating, errors.length]); |
There was a problem hiding this comment.
The useEffect hook is missing handleExternalAuthentication, code, and state in its dependency array. According to React's exhaustive-deps rule, all values used inside the effect that come from the component scope should be included in the dependency array.
Since handleExternalAuthentication is defined in the component, it should either be:
- Added to the dependency array along with
codeandstate(which would require memoizinghandleExternalAuthenticationwithuseCallback) - Or moved inside the
useEffectto avoid the dependency
The recommended fix is to move the function definition inside the useEffect:
useEffect(() => {
const externalAuthNotPerformed = !authenticating && !errors.length;
if (isExternalAuthCallback && externalAuthNotPerformed && !externalAuthProcessedRef.current) {
externalAuthProcessedRef.current = true;
const handleExternalAuthentication = async (code: string, state: string) => {
await loginByExternalPlugin!(requestedExternalPluginId, {
code,
state,
});
setRequestedExternalPluginId(null);
navigate(fallbackUri);
setFallbackUri(null);
};
handleExternalAuthentication(code!, state!);
}
}, [isExternalAuthCallback, authenticating, errors.length, loginByExternalPlugin, requestedExternalPluginId, setRequestedExternalPluginId, navigate, fallbackUri, setFallbackUri, code, state]);| // Track if external auth callback has been processed to avoid double-processing in Strict Mode | ||
| const externalAuthProcessedRef = useRef(false); |
There was a problem hiding this comment.
issue: I don't think we should create hacks to work around strict mode, if we have issue there it probably means we have something wrong with our implementation. It's worth checking ESLint warnings.
There's also a possibility we could introduce a bug here, as copilot wrote: https://github.com/saleor/saleor-dashboard/pull/6175/files#r2589250212
There was a problem hiding this comment.
As far as I see it it's not necessarily a hack for React Strict Mode per se. What should we do when component remounts and Apollo has the lazy query in flight?
There was a problem hiding this comment.
Assuming we can't have that race condition realistically outside of strict mode the ref is indeed unnecessary but what do we do then for the strict mode so it works as expected?
There was a problem hiding this comment.
What should we do when component remounts and Apollo has the lazy query in flight?
I think apollo should cancel query if it is in flight and component re-renders, if it doesn't then probably there's some bug in Apollo, it might be because we use an old version though :/
| // Guard against double-processing in React Strict Mode | ||
| if (isExternalAuthCallback && externalAuthNotPerformed && !externalAuthProcessedRef.current) { | ||
| externalAuthProcessedRef.current = true; | ||
| handleExternalAuthentication(code!, state!); | ||
| } |
There was a problem hiding this comment.
issue: again we probably have issue somewhere else, we shouldn't write specific fixes to Strict mode
|
@mirekm closing for now, please reopen if you decide you want to conitnue |
No description provided.