React Native - Flatlist 处理大数据

React Native - Flatlist Handling Large Data

问题: 我正在开发一个聊天应用程序,我试图呈现像 whatsapp 那样的项目,因为它最重要的是,永远不会向用户显示未加载的数据。如您所知,whatsapp 可以立即呈现整个消息(可能通过使用 'onEndReach')。我在平面列表中有成千上万的数据,渲染速度太慢。我在这里 https://reactnative.dev/docs/optimizing-flatlist-configuration 尝试了 React Native 的文档,它稍微提高了性能,但没有达到我的预期。因为我想更快地呈现数据,所以用户无法捕捉到加载速度并看到空白的东西。我也搜索了太多的网站,但没有找到任何关于它的记录和清晰的信息。所以我决定在这里分享我的解决方案,希望它能帮助那些遭受苦难的人。

我的解决方案:分页是对性能最有用的东西。如果您快速加载少量数据但遭受大数据的困扰,那么我认为它会有所帮助。我尝试了很多库,但大多数都没有动态高度计算。 Flatlist 有很多类似的好特性。我比较了其中的许多,实际上与开发时间和性能相比,这个解决方案是最好的。目前我有超过 1000 个数据,其中包含音频消息、图像等。如果您的所有组件都是图像,那么 react-native 建议 Fast-Image 组件。我暂时没有尝试那个案例。见代码=>


function ChatRoomScreen() {

const [offset, setOffset] = useState(1); //Its Like Page number
const [ messages, setMessages ] = useState<[]>([]); //Contains the whole data
const [dataSource, setDataSource] =  useState<[]>([]); //Contains limited number of data
const renderMessage =  function({ item }) { //Your render component
    return <Message/>
};
const keyExtractor = (item) => item.id;
const windowSize =messages.length > 50 ? messages.length/4 : 21;
let num =100 // This is the number which defines how many data will be loaded for every 'onReachEnd'
let initialLoadNumber = 40 // This is the number which defines how many data will be loaded on first open

useEffect(() => { //Initially , we set our data.

    setMessages('YOUR WHOLE ARRAY OF DATA HERE'); 

}, [])

useEffect(()=> { //Here we setting our data source on first open.

    if(dataSource.length < messages.length){  
        if(offset == 1){
            setDataSource(messages.slice(0,offset*initialLoadNumber ))
        }      
    }

}, [messages]);  

const getData = () => { // When scrolling we set data source with more data.

    if(dataSource.length < messages.length && messages.length != 0){
        setOffset(offset + 1);
        setDataSource(messages.slice(0,offset*num )) //We changed dataSource.
    }

};

return(
        <SafeAreaView style={styles.page}>
            {messages.length!=0 && <FlatList
                    data={dataSource}
                    renderItem={renderMessage}
                    inverted
                    initialNumToRender={initialLoadNumber}
                    windowSize={windowSize} //If you have scroll stuttering but working fine when 'disableVirtualization = true' then use this windowSize, it fix the stuttering problem.
                    maxToRenderPerBatch={num}
                    updateCellsBatchingPeriod={num/2}
                    keyExtractor={keyExtractor}
                    onEndReachedThreshold ={offset < 10 ? (offset*(offset == 1 ? 2 : 2)):20} //While you scolling the offset number and your data number will increases.So endReached will be triggered earlier because our data will be too many
                    onEndReached = {getData} 
                    removeClippedSubviews = {true}    
                /> 
            }       
        </SafeAreaView>
    )
};

export default ChatRoomScreen

另外不要忘记在渲染组件中执行此操作 =>

function arePropsEqual(prevProps, nextProps) {
  return prevProps.id === nextProps.id; //It could be something else not has to be id.
}
export default memo(Message,arePropsEqual); //Export with memo of course :) 

如果您不选中此项,当您更改数据时,每次您想要添加更多内容进行渲染时,您的整个数据都将 re-rendered。

有了它,即使组件很重,我的消息也像 whatsapp 一样加载。我滚动得太快了,没有空白字段。如果您在开发模式下使用 expo,也许您会看到一些空白,但我没有遇到过这种情况。如果您遇到过这种情况,那么我建议您尝试使用生产模式,它在独立应用程序上的速度要快得多,并且不可能像我看到的那样通过滚动来捕捉数据加载速度。 所以这里的主要逻辑我们从来没有把整个数据给 flatlist,我们在这里做了某种分页,它对我有用!如果您尝试这个并且对此有一个好主意,请分享并我们讨论。因为当你默认使用 flatlist 时它不是很好,所以我看到人们使用其他库而不是 React 自己的 flatlist。

更新: 我用它来加载更多而不是将整个数据放入数组中。现在它动态地将传入数据连接到我的数组

await DataStore.query(MessageModel, 
                    message => message.chatroomID("eq", chatRoom?.id),
                    {
                        sort: message => message.createdAt(SortDirection.DESCENDING),
                        page:offset,
                        limit:num,
                    }
                    
                ).then((e)=>{
                    setMessages(messages.concat(e));
                });