Neil.

滚动加载

一个简单的滚动加载实现:通过Web API IntersectionObserver观察最后一个列表元素,当它出现在可视区域时,解除观察,同时异步地加载数据,并展示一个loading,在加载数据的回调中取消loading,并更新数据,这时ScrollLoad中state.data改变,重新渲染,原尾元素被卸载,断开IntersectionObserver的连接,新的尾元素挂载为ObservableScrollItem,其他元素渲染为BaseScrollItem,如此循环;

每次加载数据会导致整个列表重新渲染,可以优化;

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>
    }
}