1- import { useState , useCallback , useEffect } from 'react' ;
1+ import { useState , useCallback , useEffect , useRef } from 'react' ;
22import { axiosClient } from '@/network/axiosClient' ;
33import { Problem } from '@/types/types' ;
44
@@ -9,7 +9,10 @@ export interface FilterState {
99 search : string | null ;
1010}
1111
12+ const PAGE_SIZE = 20 ;
13+
1214export function useFilteredProblems ( ) {
15+ // States for both filtering and pagination
1316 const [ problems , setProblems ] = useState < Problem [ ] > ( [ ] ) ;
1417 const [ filters , setFilters ] = useState < FilterState > ( {
1518 difficulty : null ,
@@ -18,32 +21,75 @@ export function useFilteredProblems() {
1821 search : null ,
1922 } ) ;
2023 const [ isLoading , setIsLoading ] = useState ( true ) ;
24+ const [ page , setPage ] = useState ( 1 ) ;
25+ const [ hasMore , setHasMore ] = useState ( true ) ;
26+ const seenIds = useRef ( new Set < number > ( ) ) ;
2127
22- const fetchProblems = useCallback ( async ( ) => {
23- setIsLoading ( true ) ;
24- const params = new URLSearchParams ( ) ;
25- if ( filters . difficulty ) params . append ( 'difficulty' , filters . difficulty ) ;
26- if ( filters . status ) params . append ( 'status' , filters . status ) ;
27- if ( filters . topics )
28- filters . topics . forEach ( ( topic ) => params . append ( 'topics' , topic ) ) ;
29- if ( filters . search ) params . append ( 'search' , filters . search ) ;
30- try {
31- const url = params . toString ( )
32- ? `/questions?${ params . toString ( ) } `
33- : '/questions' ;
34- const response = await axiosClient . get ( url ) ;
35- setProblems ( response . data ) ;
36- } catch ( error ) {
37- console . error ( 'Error fetching problems:' , error ) ;
38- } finally {
39- setIsLoading ( false ) ;
40- }
41- } , [ filters ] ) ;
28+ const fetchProblems = useCallback (
29+ async ( pageNum : number , isLoadingMore = false ) => {
30+ if ( ! isLoadingMore ) {
31+ seenIds . current . clear ( ) ;
32+ }
4233
43- useEffect ( ( ) => {
44- fetchProblems ( ) ;
45- } , [ fetchProblems ] ) ;
34+ setIsLoading ( true ) ;
35+
36+ try {
37+ const params = new URLSearchParams ( ) ;
38+ params . append ( 'page' , pageNum . toString ( ) ) ;
39+ params . append ( 'limit' , PAGE_SIZE . toString ( ) ) ;
40+
41+ // Apply filters to query
42+ if ( filters . difficulty ) params . append ( 'difficulty' , filters . difficulty ) ;
43+ if ( filters . status ) params . append ( 'status' , filters . status ) ;
44+ if ( filters . topics ?. length ) {
45+ filters . topics . forEach ( ( topic ) => params . append ( 'topics' , topic ) ) ;
46+ }
47+ if ( filters . search ) params . append ( 'search' , filters . search ) ;
48+
49+ const url = `/questions?${ params . toString ( ) } ` ;
50+ const response = await axiosClient . get < Problem [ ] > ( url ) ;
51+ const newProblems = response . data ;
52+
53+ if ( newProblems . length === 0 ) {
54+ setHasMore ( false ) ;
55+ return ;
56+ }
57+
58+ if ( isLoadingMore ) {
59+ console . log ( 'Fetching a page of 20 items' ) ;
60+ const uniqueNewProblems : Problem [ ] = [ ] ;
61+ let foundDuplicate = false ;
62+
63+ for ( const problem of newProblems ) {
64+ if ( seenIds . current . has ( problem . _id ) ) {
65+ foundDuplicate = true ;
66+ break ;
67+ }
68+ seenIds . current . add ( problem . _id ) ;
69+ uniqueNewProblems . push ( problem ) ;
70+ }
71+
72+ if ( foundDuplicate || uniqueNewProblems . length === 0 ) {
73+ setHasMore ( false ) ;
74+ }
4675
76+ setProblems ( ( prev ) => [ ...prev , ...uniqueNewProblems ] ) ;
77+ } else {
78+ newProblems . forEach ( ( problem ) => seenIds . current . add ( problem . _id ) ) ;
79+ setProblems ( newProblems ) ;
80+ setHasMore ( newProblems . length === PAGE_SIZE ) ;
81+ }
82+ } catch ( error ) {
83+ console . error ( 'Error fetching problems:' , error ) ;
84+ setHasMore ( false ) ;
85+ } finally {
86+ setIsLoading ( false ) ;
87+ }
88+ } ,
89+ [ filters ] ,
90+ ) ; // Note filters dependency
91+
92+ // Filter functions
4793 const updateFilter = useCallback (
4894 ( key : keyof FilterState , value : string | string [ ] | null ) => {
4995 setFilters ( ( prev ) => ( {
@@ -67,18 +113,29 @@ export function useFilteredProblems() {
67113 } ) ) ;
68114 } , [ ] ) ;
69115
70- const refetchFilter = useCallback ( ( ) => {
71- setFilters ( ( prev ) => ( {
72- ...prev ,
73- } ) ) ;
74- } , [ ] ) ;
116+ // Reset and fetch when filters change
117+ useEffect ( ( ) => {
118+ setPage ( 1 ) ;
119+ fetchProblems ( 1 , false ) ;
120+ } , [ filters , fetchProblems ] ) ;
121+
122+ // Load more function for infinite scroll
123+ const loadMore = useCallback ( ( ) => {
124+ if ( ! isLoading && hasMore ) {
125+ const nextPage = page + 1 ;
126+ setPage ( nextPage ) ;
127+ fetchProblems ( nextPage , true ) ;
128+ }
129+ } , [ isLoading , hasMore , page , fetchProblems ] ) ;
75130
76131 return {
77132 problems,
78133 filters,
79134 updateFilter,
80135 removeFilter,
81136 isLoading,
82- refetchFilter,
137+ hasMore,
138+ loadMore,
139+ fetchProblems,
83140 } ;
84141}
0 commit comments