@@ -33,6 +33,7 @@ import {
3333import { useTheme } from "@mui/material/styles" ;
3434import { useGitHubAuth } from "../../hooks/useGitHubAuth" ;
3535import { useGitHubData } from "../../hooks/useGitHubData" ;
36+ import { useDebounce } from "../../hooks/useDebounce" ;
3637
3738const ROWS_PER_PAGE = 10 ;
3839
@@ -79,18 +80,22 @@ const Home: React.FC = () => {
7980 const [ startDate , setStartDate ] = useState ( "" ) ;
8081 const [ endDate , setEndDate ] = useState ( "" ) ;
8182
82- // Fetch data when username, tab, or page changes
83+ // Debounce search filters (300ms delay) to prevent excessive re-renders
84+ const debouncedSearchTitle = useDebounce ( searchTitle , 300 ) ;
85+ const debouncedSelectedRepo = useDebounce ( selectedRepo , 300 ) ;
86+ const debouncedStartDate = useDebounce ( startDate , 300 ) ;
87+ const debouncedEndDate = useDebounce ( endDate , 300 ) ;
88+
89+ // Debounce username input (400ms delay) to reduce API calls
90+ const debouncedUsername = useDebounce ( username , 400 ) ;
91+
92+ // Auto-fetch data when debounced username, tab, or page changes
8393 useEffect ( ( ) => {
84- if ( username ) {
85- fetchData ( username , page + 1 , ROWS_PER_PAGE ) ;
94+ if ( debouncedUsername ) {
95+ setPage ( 0 ) ;
96+ fetchData ( debouncedUsername , 1 , ROWS_PER_PAGE ) ;
8697 }
87- } , [ tab , page ] ) ;
88-
89- const handleSubmit = ( e : React . FormEvent < HTMLFormElement > ) : void => {
90- e . preventDefault ( ) ;
91- setPage ( 0 ) ;
92- fetchData ( username , 1 , ROWS_PER_PAGE ) ;
93- } ;
98+ } , [ tab , debouncedUsername , fetchData ] ) ;
9499
95100 const handlePageChange = ( _ : unknown , newPage : number ) => {
96101 setPage ( newPage ) ;
@@ -115,24 +120,25 @@ const Home: React.FC = () => {
115120 }
116121 } ) ;
117122 }
118- if ( searchTitle ) {
123+ // Use debounced values for filtering
124+ if ( debouncedSearchTitle ) {
119125 filtered = filtered . filter ( ( item ) =>
120- item . title . toLowerCase ( ) . includes ( searchTitle . toLowerCase ( ) )
126+ item . title . toLowerCase ( ) . includes ( debouncedSearchTitle . toLowerCase ( ) )
121127 ) ;
122128 }
123- if ( selectedRepo ) {
129+ if ( debouncedSelectedRepo ) {
124130 filtered = filtered . filter ( ( item ) =>
125- item . repository_url . includes ( selectedRepo )
131+ item . repository_url . includes ( debouncedSelectedRepo )
126132 ) ;
127133 }
128- if ( startDate ) {
134+ if ( debouncedStartDate ) {
129135 filtered = filtered . filter (
130- ( item ) => new Date ( item . created_at ) >= new Date ( startDate )
136+ ( item ) => new Date ( item . created_at ) >= new Date ( debouncedStartDate )
131137 ) ;
132138 }
133- if ( endDate ) {
139+ if ( debouncedEndDate ) {
134140 filtered = filtered . filter (
135- ( item ) => new Date ( item . created_at ) <= new Date ( endDate )
141+ ( item ) => new Date ( item . created_at ) <= new Date ( debouncedEndDate )
136142 ) ;
137143 }
138144 return filtered ;
@@ -165,48 +171,41 @@ const Home: React.FC = () => {
165171
166172 return (
167173 < Container maxWidth = "lg" sx = { { mt : 4 , minHeight : "80vh" , color : theme . palette . text . primary } } >
168- { /* Auth Form */ }
174+ { /* Auth Inputs */ }
169175 < Paper elevation = { 1 } sx = { { p : 2 , mb : 4 , backgroundColor : theme . palette . background . paper } } >
170- < form onSubmit = { handleSubmit } >
171- < Box sx = { { display : "flex" , gap : 2 , flexWrap : "wrap" } } >
172- < TextField
173- label = "GitHub Username"
174- value = { username }
175- onChange = { ( e ) => setUsername ( e . target . value ) }
176- required
177- sx = { { flex : 1 , minWidth : 150 } }
178- />
179- < TextField
180- label = "Personal Access Token"
181- value = { token }
182- onChange = { ( e ) => setToken ( e . target . value ) }
183- type = "password"
184- sx = { { flex : 1 , minWidth : 150 } }
185- // Helper link to guide users on generating a GitHub Personal Access Token
186- helperText = {
187- < Link
188- href = "https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens"
189- target = "_blank"
190- rel = "noopener noreferrer"
191- sx = { {
192- fontSize : '0.75rem' ,
193- color : 'primary.main' ,
194- textDecoration : 'none' ,
195- '&:hover' : {
196- textDecoration : 'underline' ,
197- }
198- } }
199- >
200- How to generate?
201- </ Link >
202- }
203- />
204-
205- < Button type = "submit" variant = "contained" sx = { { minWidth : "120px" } } >
206- Fetch Data
207- </ Button >
208- </ Box >
209- </ form >
176+ < Box sx = { { display : "flex" , gap : 2 , flexWrap : "wrap" } } >
177+ < TextField
178+ label = "GitHub Username"
179+ value = { username }
180+ onChange = { ( e ) => setUsername ( e . target . value ) }
181+ placeholder = "Start typing to search..."
182+ sx = { { flex : 1 , minWidth : 150 } }
183+ />
184+ < TextField
185+ label = "Personal Access Token (optional)"
186+ value = { token }
187+ onChange = { ( e ) => setToken ( e . target . value ) }
188+ type = "password"
189+ sx = { { flex : 1 , minWidth : 150 } }
190+ helperText = {
191+ < Link
192+ href = "https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens"
193+ target = "_blank"
194+ rel = "noopener noreferrer"
195+ sx = { {
196+ fontSize : '0.75rem' ,
197+ color : 'primary.main' ,
198+ textDecoration : 'none' ,
199+ '&:hover' : {
200+ textDecoration : 'underline' ,
201+ }
202+ } }
203+ >
204+ How to generate?
205+ </ Link >
206+ }
207+ />
208+ </ Box >
210209 </ Paper >
211210
212211 { /* Filters */ }
0 commit comments