@@ -13,69 +13,66 @@ export function VirtualList(
13
13
props ,
14
14
) {
15
15
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 ) ;
19
17
const count = props . items . length
20
18
const list = useRef ( null ) ;
21
- const [ visible , setvisible ] = useState ( [ ] ) ;
22
- const [ listInnerStyle , setlistInnerStyle ] = useState ( ( ) => ( { } ) ) ;
19
+ const listInner = useRef ( null ) ;
23
20
const prevScrollTop = useRef ( 0 ) ;
24
- const created = useRef ( false ) ;
21
+ const [ scrollTop , setscrollTop ] = useState ( 0 ) ;
22
+ const [ listSize , setlistSize ] = useState ( props . listSize || 1000 ) ;
25
23
//
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
35
28
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 )
59
39
}
60
- // on created
61
- if ( ! created . current ) {
62
- update ( )
63
- created . current = true ;
40
+ if ( bottomSpace < 0 ) {
41
+ bottomSpace = 0
64
42
}
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
+ //
65
65
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
70
71
}
71
72
}
72
73
//
73
- useEffect ( ( ) => {
74
- update ( )
75
- } , [ ] ) ;
76
- //
77
74
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 } } >
79
76
{ visible . map ( ( item , i ) => < div key = { item . id } > { item . text } </ div > ) }
80
77
</ div >
81
78
</ div >
0 commit comments