11import { useVirtualizer } from "@tanstack/react-virtual" ;
2- import { type ReactNode , useEffect , useLayoutEffect , useRef } from "react" ;
2+ import { type ReactNode , useEffect , useRef } from "react" ;
33
44interface VirtualizedListProps < T > {
55 items : T [ ] ;
@@ -25,8 +25,6 @@ export function VirtualizedList<T>({
2525 footer,
2626} : VirtualizedListProps < T > ) {
2727 const scrollRef = useRef < HTMLDivElement > ( null ) ;
28- const isAtBottomRef = useRef ( true ) ;
29- const isInitialMountRef = useRef ( true ) ;
3028
3129 const virtualizer = useVirtualizer ( {
3230 count : items . length ,
@@ -40,91 +38,43 @@ export function VirtualizedList<T>({
4038 } ) ;
4139
4240 useEffect ( ( ) => {
43- const el = scrollRef . current ;
44- if ( ! el ) return ;
45-
46- let ticking = false ;
47- const handleScroll = ( ) => {
48- if ( ticking ) return ;
49- ticking = true ;
50- requestAnimationFrame ( ( ) => {
51- const threshold = 50 ;
52- isAtBottomRef . current =
53- el . scrollHeight - el . scrollTop - el . clientHeight < threshold ;
54- ticking = false ;
55- } ) ;
56- } ;
57-
58- el . addEventListener ( "scroll" , handleScroll , { passive : true } ) ;
59- return ( ) => el . removeEventListener ( "scroll" , handleScroll ) ;
60- } , [ ] ) ;
61-
62- useLayoutEffect ( ( ) => {
63- const el = scrollRef . current ;
64- if ( ! el || ! autoScrollToBottom || items . length === 0 ) {
65- return ;
66- }
67-
68- if ( isInitialMountRef . current ) {
69- isInitialMountRef . current = false ;
70- el . scrollTop = el . scrollHeight ;
71- return ;
72- }
73- } , [ autoScrollToBottom , items . length ] ) ;
74-
75- useLayoutEffect ( ( ) => {
76- const el = scrollRef . current ;
77- if (
78- ! el ||
79- ! autoScrollToBottom ||
80- items . length === 0 ||
81- isInitialMountRef . current ||
82- ! isAtBottomRef . current
83- ) {
84- return ;
41+ if ( autoScrollToBottom && items . length > 0 ) {
42+ virtualizer . scrollToIndex ( items . length - 1 , { align : "end" } ) ;
8543 }
86-
87- el . scrollTop = el . scrollHeight ;
88- } , [ autoScrollToBottom , items ] ) ;
44+ } , [ autoScrollToBottom , items . length , virtualizer ] ) ;
8945
9046 const virtualItems = virtualizer . getVirtualItems ( ) ;
9147
9248 return (
9349 < div
9450 ref = { scrollRef }
9551 className = { `${ className } scrollbar-hide` }
96- style = { {
97- height : "100%" ,
98- overflow : "auto" ,
99- scrollBehavior : "auto" ,
100- } }
52+ style = { { height : "100%" , overflow : "auto" } }
10153 >
102- { items . length > 0 && (
103- < div
104- style = { {
105- height : virtualizer . getTotalSize ( ) ,
106- width : "100%" ,
107- position : "relative" ,
108- } }
109- >
110- { virtualItems . map ( ( virtualRow ) => (
111- < div
112- key = { virtualRow . key }
113- ref = { virtualizer . measureElement }
114- data-index = { virtualRow . index }
115- style = { {
116- position : "absolute" ,
117- top : 0 ,
118- left : 0 ,
119- width : "100%" ,
120- transform : `translateY(${ virtualRow . start } px)` ,
121- } }
122- >
123- { renderItem ( items [ virtualRow . index ] , virtualRow . index ) }
124- </ div >
125- ) ) }
126- </ div >
127- ) }
54+ < div
55+ style = { {
56+ height : virtualizer . getTotalSize ( ) ,
57+ width : "100%" ,
58+ position : "relative" ,
59+ } }
60+ >
61+ { virtualItems . map ( ( virtualRow ) => (
62+ < div
63+ key = { virtualRow . key }
64+ ref = { virtualizer . measureElement }
65+ data-index = { virtualRow . index }
66+ style = { {
67+ position : "absolute" ,
68+ top : 0 ,
69+ left : 0 ,
70+ width : "100%" ,
71+ transform : `translateY(${ virtualRow . start } px)` ,
72+ } }
73+ >
74+ { renderItem ( items [ virtualRow . index ] , virtualRow . index ) }
75+ </ div >
76+ ) ) }
77+ </ div >
12878 { footer }
12979 </ div >
13080 ) ;
0 commit comments