FlatList renderItem被多次调用

时间:2019-10-31 21:07:30

标签: react-native

我在做什么?

尝试根据state.data中存储的某些项目呈现FlatList。存在一个按钮,按下后会在state.data中添加一个新项目。

出了什么问题?

在按下按钮时,我期望renderItem只能被调用state.data.length次,而被调用2*state.data.length次。另外,这种2x行为并不总是一致的,并且随着state.data.length的增加而变化。

Ex:当state.data.length=3时,则在初始渲染时,renderItem的调用次数恰好是3次,这与state.data中的项目数相同,并且在按下向其追加新项目的按钮时state.data和现在的state.data.length=4和renderItem被调用8次,即2*state.data.length次。

但是,当state.data.length=11时,则在初始渲染时,renderItem恰好被调用了21次,并在按下按钮时将一个新项附加到state.data到现在的state.data.length=12上,并且renderItem被称为23偏离2*state.data.length行为的时间。

enter image description here

我期望什么?

renderItem在初始和后续渲染中仅被调用state.data.length次。

我尝试了什么?

  1. 从头开始创建一个新项目,以测试这种行为,没有运气。
  2. 在renderItem PureComponent内部的组件。仍然与上述行为相同。
  3. 通过react-native-web在https://codesandbox.io/s/react-native-nn73t处创建一个CodeSandbox,其行为与以前相同。请参考此沙箱以获取代码。
  4. 使用道具,例如maxToRenderPerBatch,initialNumsToRender等,但均无济于事。尽管以相对较大的state.data来使用它们确实减少了renderItem的调用次数,但仍然没有太大的减少。
  5. 提出了类似的问题,但不清楚

我面临的真正问题是当我试图在FlatList中呈现聊天消息(通过API调用获取并将其设置在状态内)时。现在,大约有200条消息,其renderItem在800-1200次范围内被调用,这对性能造成了影响。

这种行为是否偏离了我的预期? 如果是,那么背后的逻辑是什么。如果没有,那我在做什么错了?

3 个答案:

答案 0 :(得分:1)

我仔细阅读了您的代码,并试图理解您的担忧。

(renderItem〜FlatList的prop) 注意:-使用平面列表时,您的列表项应为纯组件或应实现shouldComponentUpdate,否则它们将提供比预期更多的时间

因此,请牢记以下几点,即按照所述实施了您的平面清单项目。

  1. 根据您对性能的关注,尽管renderItem被调用了很多次,但不会有任何问题。当您检查项目实际渲染的次数(通过项目渲染中的console.log())时,行为是预期的。 因此您的 项实际上并没有得到渲染 ,只是FlatList中的renderItem被调用了很多次。
尝试在共享的项目链接中按(不可变地添加项目 文本),您将会更好地理解。 Check This project

  1. 所以现在剩下的问题是,为什么renderItem被称为意外次数,以及如何了解幕后情况?

甚至我都不知道事情是如何实现的,但是根据我在文档中阅读的内容,我只会分享一些可能有帮助的内容。

“”为了限制内存并实现流畅的滚动,将内容异步显示在屏幕外。这意味着滚动速度可能比填充速率快,并且暂时看到空白内容。这是一个折衷方案,可以进行调整以适合每个应用程序的需求,我们正在努力在幕后对其进行改进。”

这是少数道具的默认值,有助于控制权衡

maxToRenderPerBatch:10,

onEndReachedThreshold:2,//长度的倍数

scrollEventThrottle:50,

updateCellsBatchingPeriod:50,

windowSize:21,//长度的倍数

为了更清楚地理解,我们需要了解VirtualizedList的实现方式。在进一步挖掘之后,我一定会更新此答案。

答案 1 :(得分:0)

遇到同样的问题,我的问题是将箭头函数作为道具传递,我设法通过将我的所有fnProp={() => {}}替换为fnProp={someFunc}

来解决此问题。

另一种解决方案是使用shouldComponentUpdate。作为FlatList中用作renderItem道具的项目“ YourWrappedItem”,您可以使用这样的包装器组件:

class ItemToRender extends Component {
    shouldComponentUpdate() {
        return false
    }
    render() {
        return (
            <YourWrappedItem {...someProps}>
                Example
            </YourWrappedItem>
        )
    }
}

希望它对某人有帮助。

答案 2 :(得分:0)

使用 React.memo 解决这个问题

<FlatList
renderItem={({item, index}) => <DisplayRenderMemo item={item} index={index} onPress = { async() =>  { } }/>  } 
/>

const DisplayRenderMemo = React.memo(({ item, index, onPress }) => 
 {   
 const count = React.useRef(0);
  return ( 
    <TouchableOpacity 
                    key={index}   
                    onPress={onPress}>
                   <Text>{item.label} {(count.current++)} counts</Text>
    </TouchableOpacity> 
     )
 }, (prevProps, nextProps) => { 
console.log(nextProps, prevProps);
  return nextProps.item.data_id === prevProps.item.data_id 
})

首先检查不使用 React.memo 之类的

const DisplayRenderMemo = ({ item, index, onPress }) => 
     {   
     const count = React.useRef(0);
      return ( 
        <TouchableOpacity 
                        key={index}   
                        onPress={onPress}>
                       <Text>{item.label} {(count.current++)} counts</Text>
        </TouchableOpacity> 
         )
     }

现在显示为 0, 1, 2, 3 个计数

然后像上面提到的代码一样使用 React.memo

万岁!!!现在它只显示 0 计数。所以我们得到了解决方案。它没有重新渲染。

注意:请确保 {item} 数据中哪个字段是唯一的。因为我们不能将索引用作唯一的。因此,只需将任何唯一键与您的 {item} 数据一起传递。原因是如果我们获取新的数据集来替换现有数据,则该现有数据集由索引控制在 react.memo 中。因此,不会重新渲染相同的索引项以在平面列表中查看