@@ -28,6 +28,11 @@ export const Timeline = () => {
2828 skipValue ?: number ;
2929 timelineValue ?: PostView [ ] ;
3030 } ) => {
31+ // Prevent rapid-fire requests (only check finishedLoading, isLoading is checked by useInfiniteScroll)
32+ if ( finishedLoading ) {
33+ return ;
34+ }
35+
3136 setIsLoading ( true ) ;
3237
3338 try {
@@ -44,21 +49,46 @@ export const Timeline = () => {
4449 content // kind
4550 ) ;
4651
52+ // Check if we received no data
53+ if ( ! Array . isArray ( data ) || data . length === 0 ) {
54+ setFinishedLoading ( true ) ;
55+ setIsLoading ( false ) ;
56+ return ;
57+ }
58+
4759 setSkip ( skipValue + limit ) ;
4860
4961 // filter out deleted posts and muted users
5062 const filteredData = data . filter (
5163 ( post ) => ! deletedPosts . includes ( post . details . id ) && ! mutedUsers . includes ( post . details . author )
5264 ) ;
53- setTimeline ( [ ...timelineValue , ...filteredData ] ) ;
65+
66+ // If all data was filtered out and we got less than limit, we've reached the end
67+ if ( filteredData . length === 0 && data . length < limit ) {
68+ setFinishedLoading ( true ) ;
69+ setIsLoading ( false ) ;
70+ return ;
71+ }
72+
73+ // Use functional update to avoid stale closure issues
74+ // If timelineValue is explicitly empty array (initialization), replace. Otherwise append.
75+ if ( timelineValue . length === 0 && skipValue === 0 ) {
76+ setTimeline ( filteredData ) ;
77+ } else {
78+ setTimeline ( ( prev ) => [ ...prev , ...filteredData ] ) ;
79+ }
5480 } catch ( error ) {
5581 setFinishedLoading ( true ) ;
5682 } finally {
5783 setIsLoading ( false ) ;
5884 }
5985 } ;
6086
61- const loader = useInfiniteScroll ( ( ) => fetchPosts ( { skipValue : skip , timelineValue : timeline } ) , isLoading ) ;
87+ const fetchMorePosts = useCallback ( ( ) => {
88+ fetchPosts ( { skipValue : skip } ) ;
89+ } , [ skip ] ) ;
90+
91+ const loader = useInfiniteScroll ( fetchMorePosts , isLoading ) ;
6292
6393 const initializeTimeline = async ( ) => {
6494 setSkip ( 0 ) ;
0 commit comments