在React中实现虚拟列表,如何正确处理滚动?

时间:2017-10-13 00:48:25

标签: javascript reactjs

我正在尝试以与React-Virtualized相同的方式实现虚拟列表。

我已经让它工作了大部分但是当你滚动超过某个阈值时,列表会继续滚动而无需用户输入,直到它到达列表的末尾。我在做什么导致了这个?

这是Codepen

有问题的组件是VirtualList组件。

const todoHeight = 40;
const viewport = { height: 800, width: 750 };
const itemsAllowedInView = viewport.height / todoHeight;
const numItems = 10000;
const maxScrollItems = numItems - itemsAllowedInView;
const maxScrollTop = todoHeight * maxScrollItems;

class VirtualList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            currentIndex: 0,
            amountRowsToRender: itemsAllowedInView * 2,
            topPadding: 0,
            bottomPadding: maxScrollTop - viewport.height
        }
        this.lastTop = 0;
    }

    scrollFunction(currentTop) {
        if (this.lastTop === currentTop || currentTop >= maxScrollTop || currentTop < 0) { return; }

        let currentIndex = Math.floor(currentTop / todoHeight);
        let endIndex = Math.min(currentIndex + itemsAllowedInView, numItems);

        // We want to render some extra for padding.
        let bufferItems = itemsAllowedInView / 2;
        let bufferSize = bufferItems * todoHeight;

        // Create top Padding
        let topPaddingHeight = 0;
        let bottomPaddingHeight = 0;
        topPaddingHeight = Math.max(0, currentTop - (bufferItems * todoHeight));
        // bottomPaddingHeight = Math.min((currentTop - (bufferItems * todoHeight)) + itemsAllowedInView, maxScrollTop);
        bottomPaddingHeight = Math.min(maxScrollTop - currentTop - viewport.height - (bufferItems * todoHeight), maxScrollTop);

        this.lastTop = currentTop;
        this.setState({
            currentIndex: currentIndex,
            topPadding: topPaddingHeight >= 0 ? topPaddingHeight : 0,
            bottomPadding: bottomPaddingHeight >= 0 ? bottomPaddingHeight : 0,
        })
    }
    catchScroll(e) {
        this.scrollFunction.call(this, e.target.scrollTop);
    }
    paddingDiv(height) {
        return (<div className='todo' style={{ height: height + 'px' }} />);
    }
    paddingTop() {
        // if(this.state.topPadding > 0){
        return (this.paddingDiv(this.state.topPadding));
        // }
    }
    paddingBottom() {
        // if(this.state.bottomPadding > 0){
        return (this.paddingDiv(this.state.bottomPadding));
        // }
    }
    render() {
        return (
            <div
                className={'list-container'}
                onScroll={this.catchScroll.bind(this)}
            >
                <div className={'inner-list'}>
                    {this.paddingTop.call(this)}
                    {!!this.props.todos && this.props.todos.slice(this.state.currentIndex, this.state.currentIndex + this.state.amountRowsToRender).map((todo, i) =>
                        <Todo content={todo.content}
                            height={todo.height ? todo.height : null}
                            id={i}
                            key={i}
                            completed={todo.completed}
                          />
                    )}
                    {this.paddingBottom.call(this)}
                </div>
            </div>
        )
    }
}

0 个答案:

没有答案