一个简单的滚动加载实现:通过Web API IntersectionObserver观察最后一个列表元素,当它出现在可视区域时,解除观察,同时异步地加载数据,并展示一个loading,在加载数据的回调中取消loading,并更新数据,这时ScrollLoad中state.data改变,重新渲染,原尾元素被卸载,断开IntersectionObserver的连接,新的尾元素挂载为ObservableScrollItem,其他元素渲染为BaseScrollItem,如此循环;
每次加载数据会导致整个列表重新渲染,可以优化;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 const fetchMockData = ( ) => { return new Promise ((resolve ) => { setTImeout (() => { resolve (new Array (1 ).fill ("sssssssssssss" )); }, 2000 ); }) } class BaseScrollItem extends React.Component { render ( ) { return <div className ="scrollitem" ref ={this.props.internalRef} > {this.props.children}</div > } } class ObservableScrollItem extends React.Component { constructor (props ) { super (props); this .ref = React .createRef (null ); } componentDidMount ( ) { const node = this .ref .current ; this .observer = new IntersectionObserver (([entry], observer ) => { if (entry.isIntersecting ) { observer.unobserve (node); this .props .loadItems (); } }); this .observer .observe (node); } componentWillUnmount ( ) { this .observer .disconnect (); } render ( ) { return <BaseScrollItem internalRef ={this.ref} > {this.props.children}</BaseScrollItem > } } export class ScrollLoad extends React.Component { constructor (props ) { super (props); this .state = { loading : true , data : new Array (5 ).fill ("sdasadsadsads" ) }; } loadItems = async () => { this .setState ({ loading : true }); const value = await fetchMockData (); this .setState ({ loading : false , data : [...this .state .data , ...value] }); } render ( ) { return <div > { this.state.data.map((item, index, arr) => { return index === arr.length - 1 ? <ObservableScrollItem loadItems ={this.loadItems} > {index}-----{item}</ObservableScrollItem > // 其实React默认会为Array Children挨个添加值为index的key,这里写不写无所谓 : <BaseScrollItem key ={index} > {index}-----{item}</BaseScrollItem > }) } { this.state.loading && <BaseScrollItem > Loading...</BaseScrollItem > } </div > } }