Skip to content

Commit 1559f1d

Browse files
committed
fix(hooks): stabilize useGitHubRepos callback dependencies
- Extract access_token from session to use as stable primitive dependency - Use useMemo for headers object based on stable access_token - All callbacks now depend on memoized headers instead of session object - Use functional state update in fetchRepos to avoid stale closure Prevents unnecessary re-renders when session object reference changes.
1 parent 719c01b commit 1559f1d

1 file changed

Lines changed: 14 additions & 19 deletions

File tree

frontend/src/hooks/useGitHubRepos.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useCallback } from 'react';
1+
import { useState, useCallback, useMemo } from 'react';
22
import { useAuth } from '@/contexts/AuthContext';
33

44
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
@@ -33,16 +33,18 @@ export function useGitHubRepos() {
3333
const [loading, setLoading] = useState(false);
3434
const [error, setError] = useState<string | null>(null);
3535

36-
const getHeaders = useCallback(() => {
37-
if (!session?.access_token) return null;
36+
// Extract access_token to stabilize dependencies
37+
const accessToken = session?.access_token;
38+
39+
const headers = useMemo(() => {
40+
if (!accessToken) return null;
3841
return {
39-
'Authorization': `Bearer ${session.access_token}`,
42+
'Authorization': `Bearer ${accessToken}`,
4043
'Content-Type': 'application/json',
4144
};
42-
}, [session]);
45+
}, [accessToken]);
4346

4447
const checkStatus = useCallback(async () => {
45-
const headers = getHeaders();
4648
if (!headers) {
4749
setStatus({ connected: false, username: null, avatar_url: null });
4850
return { connected: false, username: null, avatar_url: null };
@@ -68,10 +70,9 @@ export function useGitHubRepos() {
6870
} finally {
6971
setLoading(false);
7072
}
71-
}, [getHeaders]);
73+
}, [headers]);
7274

7375
const initiateConnect = useCallback(async () => {
74-
const headers = getHeaders();
7576
if (!headers) {
7677
setError('Not authenticated');
7778
return null;
@@ -88,7 +89,6 @@ export function useGitHubRepos() {
8889
}
8990

9091
const data = await response.json();
91-
// Store state in sessionStorage for callback verification
9292
sessionStorage.setItem('github_oauth_state', data.state);
9393
return data.auth_url;
9494
} catch (err) {
@@ -98,16 +98,14 @@ export function useGitHubRepos() {
9898
} finally {
9999
setLoading(false);
100100
}
101-
}, [getHeaders]);
101+
}, [headers]);
102102

103103
const completeConnect = useCallback(async (code: string, state: string) => {
104-
const headers = getHeaders();
105104
if (!headers) {
106105
setError('Not authenticated');
107106
return false;
108107
}
109108

110-
// Verify state matches what we stored
111109
const storedState = sessionStorage.getItem('github_oauth_state');
112110
if (state !== storedState) {
113111
setError('Invalid OAuth state - possible CSRF attack');
@@ -131,7 +129,6 @@ export function useGitHubRepos() {
131129
const data = await response.json();
132130
sessionStorage.removeItem('github_oauth_state');
133131

134-
// Update status
135132
setStatus({
136133
connected: true,
137134
username: data.username,
@@ -146,10 +143,9 @@ export function useGitHubRepos() {
146143
} finally {
147144
setLoading(false);
148145
}
149-
}, [getHeaders]);
146+
}, [headers]);
150147

151148
const disconnect = useCallback(async () => {
152-
const headers = getHeaders();
153149
if (!headers) return false;
154150

155151
try {
@@ -174,10 +170,9 @@ export function useGitHubRepos() {
174170
} finally {
175171
setLoading(false);
176172
}
177-
}, [getHeaders]);
173+
}, [headers]);
178174

179175
const fetchRepos = useCallback(async (page = 1, includeForks = false) => {
180-
const headers = getHeaders();
181176
if (!headers) {
182177
setError('Not authenticated');
183178
return [];
@@ -200,7 +195,7 @@ export function useGitHubRepos() {
200195
}
201196

202197
const data = await response.json();
203-
setRepos(page === 1 ? data : [...repos, ...data]);
198+
setRepos(prev => page === 1 ? data : [...prev, ...data]);
204199
return data;
205200
} catch (err) {
206201
const message = err instanceof Error ? err.message : 'Unknown error';
@@ -209,7 +204,7 @@ export function useGitHubRepos() {
209204
} finally {
210205
setLoading(false);
211206
}
212-
}, [getHeaders, repos]);
207+
}, [headers]);
213208

214209
const clearError = useCallback(() => setError(null), []);
215210

0 commit comments

Comments
 (0)