@@ -13,69 +13,66 @@ export function VirtualList(
1313 props ,
1414) {
1515 const buffer = props . buffer || 100
16- let scrollTop = 0
17- const itemSize = props . itemSize || 100
18- let listSize = props . listSize || 1000
16+ const [ itemSize , setitemSize ] = useState ( props . itemSize || 100 ) ;
1917 const count = props . items . length
2018 const list = useRef ( null ) ;
21- const [ visible , setvisible ] = useState ( [ ] ) ;
22- const [ listInnerStyle , setlistInnerStyle ] = useState ( ( ) => ( { } ) ) ;
19+ const listInner = useRef ( null ) ;
2320 const prevScrollTop = useRef ( 0 ) ;
24- const created = useRef ( false ) ;
21+ const [ scrollTop , setscrollTop ] = useState ( 0 ) ;
22+ const [ listSize , setlistSize ] = useState ( props . listSize || 1000 ) ;
2523 //
26- const update = ( ) => {
27- if ( created . current ) {
28- scrollTop = list . current ! . scrollTop
29- listSize = list . current ! . clientHeight
30- }
31- const totalSpace = itemSize * count
32- let topSpace = scrollTop - buffer
33- let bottomSpace = totalSpace - scrollTop - listSize - buffer
34- let startIndex , endIndex
24+ const totalSpace = itemSize * count
25+ let topSpace = scrollTop - buffer
26+ let bottomSpace = totalSpace - scrollTop - listSize - buffer
27+ let startIndex , endIndex
3528
36- if ( topSpace <= 0 ) {
37- topSpace = 0
38- startIndex = 0
39- } else {
40- startIndex = Math . floor ( topSpace / itemSize )
41- }
42- if ( totalSpace <= listSize ) {
43- endIndex = count
44- } else {
45- endIndex = count - Math . floor ( bottomSpace / itemSize )
46- }
47- if ( bottomSpace < 0 ) {
48- bottomSpace = 0
49- }
50- const visible = props . items . slice ( startIndex , endIndex )
51- let listInnerStyle = { paddingTop : `${ topSpace } px` , boxSizing : 'border-box' }
52- if ( bottomSpace < itemSize * 5 ) {
53- listInnerStyle [ 'paddingBottom' ] = `${ bottomSpace } px`
54- } else {
55- listInnerStyle [ 'height' ] = `${ totalSpace } px`
56- }
57- setvisible ( visible )
58- setlistInnerStyle ( listInnerStyle )
29+ if ( topSpace <= 0 ) {
30+ topSpace = 0
31+ startIndex = 0
32+ } else {
33+ startIndex = Math . floor ( topSpace / itemSize )
34+ }
35+ if ( totalSpace <= listSize ) {
36+ endIndex = count
37+ } else {
38+ endIndex = count - Math . floor ( bottomSpace / itemSize )
5939 }
60- // on created
61- if ( ! created . current ) {
62- update ( )
63- created . current = true ;
40+ if ( bottomSpace < 0 ) {
41+ bottomSpace = 0
6442 }
43+ const visible = props . items . slice ( startIndex , endIndex )
44+ const listInnerStyle = { paddingTop : `${ topSpace } px` , boxSizing : 'border-box' }
45+ if ( bottomSpace < itemSize * 5 ) {
46+ listInnerStyle [ 'paddingBottom' ] = `${ bottomSpace } px`
47+ } else {
48+ listInnerStyle [ 'height' ] = `${ totalSpace } px`
49+ }
50+ useEffect ( ( ) => {
51+ setlistSize ( list . current ! . clientHeight )
52+ if ( props . itemSize == null ) {
53+ // get avg item size
54+ let count = 0
55+ let totalHeight = 0
56+ for ( const el of listInner . current . children ) {
57+ const style = getComputedStyle ( el )
58+ totalHeight += el . offsetHeight + parseFloat ( style . marginTop ) + parseFloat ( style . marginBottom )
59+ count ++
60+ }
61+ setitemSize ( totalHeight / count )
62+ }
63+ } , [ props . itemSize , props . items ] ) ;
64+ //
6565 const handleScroll = ( ) => {
66- scrollTop = list . current ! . scrollTop
67- if ( Math . abs ( prevScrollTop . current - scrollTop ) > itemSize ) {
68- update ( )
69- prevScrollTop . current = scrollTop
66+ setlistSize ( list . current ! . clientHeight )
67+ const scrollTop2 = list . current ! . scrollTop
68+ if ( Math . abs ( prevScrollTop . current - scrollTop2 ) > itemSize ) {
69+ setscrollTop ( scrollTop2 )
70+ prevScrollTop . current = scrollTop2
7071 }
7172 }
7273 //
73- useEffect ( ( ) => {
74- update ( )
75- } , [ ] ) ;
76- //
7774 return < div ref = { list } onScroll = { handleScroll } style = { { height : '500px' , overflow : 'auto' , } } >
78- < div style = { listInnerStyle } >
75+ < div ref = { listInner } style = { { display : 'flex' , flexDirection : 'column' , ... listInnerStyle } } >
7976 { visible . map ( ( item , i ) => < div key = { item . id } > { item . text } </ div > ) }
8077 </ div >
8178 </ div >
0 commit comments