1- import { useState , useCallback , useRef } from 'react' ;
1+ import { useState , useCallback , useRef , useEffect } from 'react' ;
22import { Octokit } from '@octokit/core' ;
33
44interface GitHubItem {
@@ -34,14 +34,16 @@ export const useGitHubData = (
3434
3535 // Prevent stale responses overwriting latest data
3636 const lastRequestId = useRef ( 0 ) ;
37+ const abortControllerRef = useRef < AbortController | null > ( null ) ;
3738
3839 const fetchPaginated = async (
3940 octokit : Octokit ,
4041 username : string ,
4142 type : 'issue' | 'pr' ,
4243 page = 1 ,
4344 perPage = 10 ,
44- filters : FetchFilters = { }
45+ filters : FetchFilters = { } ,
46+ signal ?: AbortSignal
4547 ) => {
4648 let q = `author:${ username } is:${ type } ` ;
4749
@@ -77,6 +79,9 @@ export const useGitHubData = (
7779 order : 'desc' ,
7880 per_page : perPage ,
7981 page,
82+ request : {
83+ signal,
84+ } ,
8085 }
8186 ) ;
8287
@@ -100,6 +105,14 @@ export const useGitHubData = (
100105 return ;
101106 }
102107
108+ // Cancel any active in-flight request before triggering a new one
109+ if ( abortControllerRef . current ) {
110+ abortControllerRef . current . abort ( ) ;
111+ }
112+
113+ const controller = new AbortController ( ) ;
114+ abortControllerRef . current = controller ;
115+
103116 const requestId = ++ lastRequestId . current ;
104117
105118 setLoading ( true ) ;
@@ -122,7 +135,8 @@ export const useGitHubData = (
122135 'issue' ,
123136 page ,
124137 perPage ,
125- filters
138+ filters ,
139+ controller . signal
126140 )
127141 ) ;
128142 }
@@ -135,15 +149,16 @@ export const useGitHubData = (
135149 'pr' ,
136150 page ,
137151 perPage ,
138- filters
152+ filters ,
153+ controller . signal
139154 )
140155 ) ;
141156 }
142157
143158 const results = await Promise . allSettled ( requests ) ;
144159
145- // Ignore stale requests
146- if ( requestId !== lastRequestId . current ) {
160+ // Ignore stale or aborted requests
161+ if ( requestId !== lastRequestId . current || abortControllerRef . current !== controller ) {
147162 return ;
148163 }
149164
@@ -156,6 +171,10 @@ export const useGitHubData = (
156171 setIssues ( issueResult . value . items ) ;
157172 setTotalIssues ( issueResult . value . total ) ;
158173 } else {
174+ const reason = issueResult . reason ;
175+ if ( reason && reason . name === 'AbortError' ) {
176+ return ;
177+ }
159178 setIssues ( [ ] ) ;
160179 setTotalIssues ( 0 ) ;
161180 }
@@ -170,6 +189,10 @@ export const useGitHubData = (
170189 setPrs ( prResult . value . items ) ;
171190 setTotalPrs ( prResult . value . total ) ;
172191 } else {
192+ const reason = prResult . reason ;
193+ if ( reason && reason . name === 'AbortError' ) {
194+ return ;
195+ }
173196 setPrs ( [ ] ) ;
174197 setTotalPrs ( 0 ) ;
175198 }
@@ -180,22 +203,33 @@ export const useGitHubData = (
180203 ) ;
181204
182205 if ( hasRejected ) {
206+ const wasAborted = results . some (
207+ ( result ) => result . status === 'rejected' && result . reason ?. name === 'AbortError'
208+ ) ;
209+ if ( wasAborted ) {
210+ return ;
211+ }
183212 setError (
184213 'Some GitHub data could not be fetched completely.'
185214 ) ;
186215 }
187216
188217 setRateLimited ( false ) ;
189218 } catch ( err : unknown ) {
190- if ( requestId !== lastRequestId . current ) {
219+ if ( requestId !== lastRequestId . current || abortControllerRef . current !== controller ) {
191220 return ;
192221 }
193222
194223 const error = err as {
195224 status ?: number ;
196225 message ?: string ;
226+ name ?: string ;
197227 } ;
198228
229+ if ( error . name === 'AbortError' ) {
230+ return ;
231+ }
232+
199233 const errorMessage =
200234 error . message ?. toLowerCase ( ) || '' ;
201235
@@ -231,14 +265,23 @@ export const useGitHubData = (
231265 ) ;
232266 }
233267 } finally {
234- if ( requestId === lastRequestId . current ) {
268+ if ( requestId === lastRequestId . current && abortControllerRef . current === controller ) {
235269 setLoading ( false ) ;
236270 }
237271 }
238272 } ,
239273 [ getOctokit , rateLimited ]
240274 ) ;
241275
276+ // Cleanup abort controller on component unmount
277+ useEffect ( ( ) => {
278+ return ( ) => {
279+ if ( abortControllerRef . current ) {
280+ abortControllerRef . current . abort ( ) ;
281+ }
282+ } ;
283+ } , [ ] ) ;
284+
242285 return {
243286 issues,
244287 prs,
0 commit comments