1- import { useEffect , useState } from 'react' ;
1+ import { useEffect , useState , useRef } from 'react' ;
22import { useNavigate , useSearchParams } from 'react-router-dom' ;
33import { Loader2 , CheckCircle2 , XCircle } from 'lucide-react' ;
44import { useGitHubRepos } from '@/hooks/useGitHubRepos' ;
@@ -9,8 +9,17 @@ export function GitHubCallbackPage() {
99 const { completeConnect } = useGitHubRepos ( ) ;
1010 const [ status , setStatus ] = useState < 'processing' | 'success' | 'error' > ( 'processing' ) ;
1111 const [ errorMessage , setErrorMessage ] = useState < string > ( '' ) ;
12+
13+ const callbackRanRef = useRef ( false ) ;
14+ const timeoutRef = useRef < NodeJS . Timeout | null > ( null ) ;
1215
1316 useEffect ( ( ) => {
17+ // Prevent double-execution in React StrictMode
18+ if ( callbackRanRef . current ) return ;
19+ callbackRanRef . current = true ;
20+
21+ let mounted = true ;
22+
1423 const handleCallback = async ( ) => {
1524 const code = searchParams . get ( 'code' ) ;
1625 const state = searchParams . get ( 'state' ) ;
@@ -19,25 +28,32 @@ export function GitHubCallbackPage() {
1928
2029 // Handle GitHub OAuth errors
2130 if ( error ) {
22- setStatus ( 'error' ) ;
23- setErrorMessage ( errorDescription || error || 'GitHub authorization failed' ) ;
31+ if ( mounted ) {
32+ setStatus ( 'error' ) ;
33+ setErrorMessage ( errorDescription || error || 'GitHub authorization failed' ) ;
34+ }
2435 return ;
2536 }
2637
2738 if ( ! code || ! state ) {
28- setStatus ( 'error' ) ;
29- setErrorMessage ( 'Missing authorization code or state' ) ;
39+ if ( mounted ) {
40+ setStatus ( 'error' ) ;
41+ setErrorMessage ( 'Missing authorization code or state' ) ;
42+ }
3043 return ;
3144 }
3245
3346 // Exchange code for token via backend
3447 const success = await completeConnect ( code , state ) ;
3548
49+ if ( ! mounted ) return ;
50+
3651 if ( success ) {
3752 setStatus ( 'success' ) ;
38- // Redirect to dashboard after brief success message
39- setTimeout ( ( ) => {
40- navigate ( '/dashboard' , { replace : true } ) ;
53+ timeoutRef . current = setTimeout ( ( ) => {
54+ if ( mounted ) {
55+ navigate ( '/dashboard' , { replace : true } ) ;
56+ }
4157 } , 1500 ) ;
4258 } else {
4359 setStatus ( 'error' ) ;
@@ -46,6 +62,13 @@ export function GitHubCallbackPage() {
4662 } ;
4763
4864 handleCallback ( ) ;
65+
66+ return ( ) => {
67+ mounted = false ;
68+ if ( timeoutRef . current ) {
69+ clearTimeout ( timeoutRef . current ) ;
70+ }
71+ } ;
4972 } , [ searchParams , completeConnect , navigate ] ) ;
5073
5174 return (
0 commit comments