遍历onLayout调用时遇到的问题,该问题需要为FlatList的getItemLayout提供项目的行高

时间:2019-02-06 20:24:36

标签: react-native react-native-flatlist

我有一个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次)。 getItemDimensions()不会由onLayout调用,因此无法获取尺寸,并且currentItem的状态也不会高级-因此呈现停止。我不明白的是,为什么对于屏幕上出现的最后一个视图,onLayout都不调用getItemDimensions。

有人对迭代为何停止或以更有效的方式实现我的目标有任何见解吗?我需要向我的FlatList提供getItemLayout,因为在用户手动滚动整个FlatList之前,应用需要能够ScrollToIndex。

1 个答案:

答案 0 :(得分:0)

onLayout不被调用的问题是由于onLayout不会发生,除非先前的布局与替换前的布局之间存在差异。在我的情况下,当两个具有相同高度的视图紧随其后时,由于不会再次调用onLayout,因此迭代将停止。

简单的解决方案是根据以下问题的答案为每个视图赋予唯一的ID:Forcing onLayout on React Native view。即:

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

这样做,视图将在每次渲染调用时进行布局,因此每次也会调用onLayout。

很不幸,这似乎是生成getItemLayout()允许在具有可变高度的FlatList上进行ScrollTo操作所需的所需高度和偏移信息的唯一方法。这个解决方案虽然可以使用,但最终速度太慢了,以至于我无法使用18000个列表项,而且鉴于我的研究还没有找到React-Native的替代方案,因此我现在强烈考虑将我的应用程序恢复为纯本机开发。