11import { ghFetch as gh } from "../../lib/githubFetch" ;
22import { useEffect , useState } from "react" ;
33
4+ type Contributor = {
5+ id : number ;
6+ login : string ;
7+ avatar_url : string ;
8+ html_url : string ;
9+ contributions : number ;
10+ } ;
11+
412export default function Contributors ( ) {
5- const [ contributors , setContributors ] = useState < any [ ] > ( [ ] ) ;
13+ const [ contributors , setContributors ] = useState < Contributor [ ] > ( [ ] ) ;
614 const [ error , setError ] = useState < string > ( "" ) ;
715 const [ loading , setLoading ] = useState < boolean > ( true ) ;
816
917 useEffect ( ( ) => {
10- async function getToken ( ) {
11- return (
12- localStorage . getItem ( "gh_pat" ) ||
13- localStorage . getItem ( "github_pat" ) ||
14- localStorage . getItem ( "token " ) ||
15- ""
16- ) ;
17- }
18+ const ac = new AbortController ( ) ;
19+ let isMounted = true ;
20+
21+ const getToken = ( ) =>
22+ localStorage . getItem ( "gh_pat " ) ||
23+ localStorage . getItem ( "github_pat" ) ||
24+ localStorage . getItem ( "token" ) ||
25+ "" ;
1826
1927 async function resolveRepoFullName ( token : string ) : Promise < string | null > {
2028 // Try to find the repo by searching both org and user scopes and dash/underscore variants
@@ -53,11 +61,7 @@ export default function Contributors() {
5361 setLoading ( true ) ;
5462 setError ( "" ) ;
5563 setContributors ( [ ] ) ;
56- const token =
57- localStorage . getItem ( "gh_pat" ) ||
58- localStorage . getItem ( "github_pat" ) ||
59- localStorage . getItem ( "token" ) ||
60- "" ;
64+ const token = getToken ( ) ;
6165
6266 try {
6367 const fallback = "ASR1015/github_tracker" ;
@@ -72,6 +76,7 @@ export default function Contributors() {
7276 // Some repos return 204 No Content when there are no contributors.
7377 // res.ok is true for 204, but res.json() would throw; handle it explicitly.
7478 if ( res . status === 204 ) {
79+ if ( ! isMounted ) return ;
7580 setContributors ( [ ] ) ;
7681 return ;
7782 }
@@ -82,19 +87,26 @@ export default function Contributors() {
8287 }
8388
8489 const data = await res . json ( ) ;
85- setContributors ( Array . isArray ( data ) ? data : [ ] ) ;
90+ if ( ! isMounted ) return ;
91+ setContributors ( Array . isArray ( data ) ? ( data as Contributor [ ] ) : [ ] ) ;
8692 } catch ( err : any ) {
87- setError (
88- err ?. message ||
89- "Failed to fetch contributors. Check owner/repo and token (use 'repo' scope if private)."
90- ) ;
93+ if ( isMounted )
94+ setError (
95+ err ?. message ||
96+ "Failed to fetch contributors. Check owner/repo and token (use 'repo' scope if private)."
97+ ) ;
9198 console . error ( "[contributors] error" , err ) ;
9299 } finally {
93- setLoading ( false ) ;
100+ if ( isMounted ) setLoading ( false ) ;
94101 }
95102 }
96103
97104 loadContributors ( ) ;
105+
106+ return ( ) => {
107+ isMounted = false ;
108+ ac . abort ( ) ;
109+ } ;
98110 } , [ ] ) ;
99111
100112 return (
0 commit comments