Scrolling issues with FlatList when rows are variable height

南楼画角 提交于 2019-12-02 21:01:00

So what I think you can do and what you already have the outlets for is to store a collection by the index of the rows layouts onLayout. You'll want to store the attributes that's returned by getItemLayout: {length: number, offset: number, index: number}.

Then when you implement getItemLayout which passes an index you can return the layout that you've stored. This should resolve the issues with scrollToIndex. Haven't tested this, but this seems like the right approach.

Use scrollToOffset() instead:

export default class List extends React.PureComponent {

    // Gets the total height of the elements that come before
    // element with passed index
    getOffsetByIndex(index) {
        let offset = 0;
        for (let i = 0; i < index; i += 1) {
            const elementLayout = this._layouts[i];
            if (elementLayout && elementLayout.height) {
                offset += this._layouts[i].height;
            }
        }
        return offset;
    }

    // Gets the comment object and if it is a comment
    // is in the list, then scrolls to it
    scrollToComment(comment) {
        const { list } = this.props;
        const commentIndex = list.findIndex(({ id }) => id === comment.id);
        if (commentIndex !== -1) {
            const offset = this.getOffsetByIndex(commentIndex);
            this._flatList.current.scrollToOffset({ offset, animated: true });
        }
    }

    // Fill the list of objects with element sizes
    addToLayoutsMap(layout, index) {
        this._layouts[index] = layout;
    }

    render() {
        const { list } = this.props;

        return (
            <FlatList
                data={list}
                keyExtractor={item => item.id}
                renderItem={({ item, index }) => {
                    return (
                        <View
                            onLayout={({ nativeEvent: { layout } }) => {
                                this.addToLayoutsMap(layout, index);
                            }}
                        >
                            <Comment id={item.id} />
                        </View>
                    );
                }}
                ref={this._flatList}
            />
        );
    }
}

1) When rendering, I get the size of each element of the list and write it into an array:

onLayout={({ nativeEvent: { layout } }) => this._layouts[index] = layout}

2) When it is necessary to scroll the screen to the element, I summarize the heights of all the elements in front of it and get the amount to which to scroll the screen (getOffsetByIndex method).

3) I use the scrollToOffset method:

this._flatList.current.scrollToOffset({ offset, animated: true });

(this._flatList is ref of FlatList)

Have you tried scrollToEnd?

http://facebook.github.io/react-native/docs/flatlist.html#scrolltoend

As the documentation states, it may be janky without getItemLayout but for me it does work without it

I did not find any way to use getItemLayout when the rows have variable heights , So you can not use initialScrollIndex .

But I have a solution that may be a bit slow: You can use scrollToIndex , but when your item is rendered . So you need initialNumToRender . You have to wait for the item to be rendered and after use scrollToIndex so you can not use scrollToIndex in componentDidMount .

The only solution that comes to my mind is using scrollToIndex in onViewableItemsChanged . Take note of the example below : In this example, we want to go to item this.props.index as soon as this component is run

constructor(props){

        this.goToIndex = true;

    }

render() {
        return (

                <FlatList
                    ref={component => {this.myFlatList = component;}}
                    data={data}
                    renderItem={({item})=>this._renderItem(item)}
                    keyExtractor={(item,index)=>index.toString()}
                    initialNumToRender={this.props.index+1}
                    onViewableItemsChanged={({ viewableItems }) => {
                        if (this.goToIndex){
                            this.goToIndex = false;
                            setTimeout(() => { this.myFlatList.scrollToIndex({index:this.props.index}); }, 10);
                        }
                    }}
                />

        );
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!