遍历 onLayout 调用以向 FlatList 的 getItemLayout 提供项目的行高时出现问题

Problem iterating through onLayout calls for providing row heights of items to FlatList's getItemLayout

我有一个 FlatList,其中填充了大约 18000 个文本行。因为每一行的文本都是可变长度的,所以我无法提供用于 getItemLayout 的静态高度。

我试图做的是首先使用虚拟视图遍历整个数据集,我通过 onLayout 从中收集高度尺寸。我的问题是,目前,该视图在停止之前仅重新渲染了大约 10 次。据我所知,最终出现在屏幕上的最后一个视图并没有调用 onLayout —— 这又是第 10 个(大约是第 10 个,具体取决于我遍历数据的时间间隔)。

下面是我的代码,我在这里尝试对其进行了一些简化。目前只是想找到一种方法来在单独的组件中渲染之前遍历和预先计算整个数据集的视图高度。

import React, { Component } from 'react';
import { AppRegistry, Text, View, TouchableOpacity, StyleSheet } from 'react-native';


export default class DummyMainItem extends Component {
constructor(props) {
    super(props);
    this.state = {
        codeData: [],
        currentItem: -1,
    };
}

componentWillReceiveProps(nextProps) {
    // Get data from MainFlatList
    if (nextProps.codeData != '') {
        console.log("PROPS in DummyMainItem are codeData of length: " + nextProps.codeData.length);
        this.setState({ codeData: nextProps.codeData });
    }
}


getItemDimensions(layout) {
    const { x, y, width, height } = layout;
    curItem = this.state.currentItem;
    console.log("getItemDimensions of #" + curItem + " (width, height) : (" + width + ", " + height + ")");

    if (curItem <= this.state.codeData.length) {
        this.setState({ currentItem: curItem + 1 });
    }
}

render() {
    console.log("Dummy Rendering item #" + this.state.currentItem);

    if (this.state.codeData != null && this.state.codeData.length > 0) {

        var item = this.state.codeData[this.state.currentItem];

        switch (item.type) {
            case '0':
                console.log("Dummy Case 0 FULLTEXT: " + item.fulltext);
                return (
                    <TouchableOpacity>
                        <View onLayout={(event) => { this.getItemDimensions(event.nativeEvent.layout) }}>
                            <View>
                                <Text>{item.fulltext}</Text>
                            </View>
                        </View>
                    </TouchableOpacity>
                );

            case '1':
                console.log("Dummy Case 1 FULLTEXT: " + item.fulltext);
                return (
                    <TouchableOpacity>
                        <View onLayout={(event) => { this.getItemDimensions(event.nativeEvent.layout) }}>
                            <View>
                                <Text>{item.fulltext}</Text>
                            </View>
                        </View>
                    </TouchableOpacity>
                );
            case '2':
                console.log("Dummy Case 2 FULLTEXT: " + item.fulltext);
                return (
                    <TouchableOpacity>
                        <View onLayout={(event) => { this.getItemDimensions(event.nativeEvent.layout) }}>
                            <View style={{ flexDirection: 'row' }}>
                                <Text>{item.section}</Text>
                                <Text>{item.fulltext}</Text>
                            </View>
                        </View>
                    </TouchableOpacity>
                );

            default:
                return (
                    <TouchableOpacity>
                        <View>
                            <View onLayout={(event) => { this.getItemDimensions(event.nativeEvent.layout) }}>
                                <Text>{item.type} {item.fulltext} </Text>
                            </View>
                        </View>
                    </TouchableOpacity>
                );
        }

    } else {
        console.log("Dummy Case Blank Render");
        return (
            <TouchableOpacity>
                <View onLayout={(event) => { this.getItemDimensions(event.nativeEvent.layout) }}>
                    <View>
                        <Text> </Text>
                    </View>
                </View>
            </TouchableOpacity>
        )
    }

}
}

AppRegistry.registerComponent('DummyMainItem', () => DummyMainItem);

我希望我的代码每次渲染视图时都会调用 getItemDimensions() 函数,它本身会调整 this.state.currentItem 的状态,这种状态变化会导致 render() 函数再次触发。

实际行为是执行上述操作,但次数有限(比我需要的 18000 更接近 10 次)。 onLayout 未调用 getItemDimensions(),因此未获取尺寸且 currentItem 的状态未提前 - 因此渲染停止。我不明白的是为什么对于屏幕上出现的最后一个视图,onLayout 没有调用 getItemDimensions。

有没有人知道迭代停止的原因,或者更有效的方法来实现我的目标?我需要为我的 FlatList 提供 getItemLayout,因为应用程序需要能够在用户手动滚动整个 FlatList 之前使用 ScrollToIndex。

onLayout 未被调用的问题是由于除非前一个布局与替换它的布局之间存在差异,否则 onLayout 不会发生。在我的例子中,当 2 个具有相同高度的视图紧随其后时,由于没有再次调用 onLayout,迭代将停止。

简单的解决方案是根据对这个问题的回答给每个视图一个唯一的 ID:。即:

<View onLayout={(event) => { this.getItemDimensions(event.nativeEvent.layout) }} 
    key={this.state.currentItem}>

通过这样做,每次渲染调用都会对视图进行布局,因此每次也会调用 onLayout。

不幸的是,这似乎是生成 getItemLayout() 允许对具有可变高度的 FlatLists 进行 ScrollTo 操作所需的高度和偏移信息的唯一方法。这个解决方案虽然有效,但最终太慢了,对我有 18000 个列表项没有用,并且鉴于我的研究还没有找到 React-Native 的任何替代方案,我现在强烈考虑将我的应用程序返回到纯原生发展。