React Native FlatList onScroll 对 Expo 应用程序中的大数据集变得无响应

问题描述 投票:0回答:1

我正在开发一个 Expo React Native 应用程序,该应用程序具有页眉、页脚和根据滚动方向设置动画的操作按钮切换。该应用程序的主页上有两个选项卡:“社交帖子”和“排名”。 “社交帖子”选项卡显示从 API 加载的动态内容,而“排名”选项卡是静态的。

我面临的问题是“社交帖子”选项卡,它使用

FlatList
显示大量数据。当列表到达末尾时,它会使用
onEndReached
加载更多数据。向下滚动时,页眉、页脚和按钮会以动画方式显示在视图之外,这按预期工作。但是,随着数据变大,
onScroll
中的
FlatList
事件变得无响应并且不会持续触发。

这种不一致会导致页眉、页脚和按钮卡在最后的状态并停止响应滚动事件。我尝试了各种性能优化,包括在每个列表项上使用

useMemo
useCallback
,但问题仍然存在。当数据集很大时,滚动体验会变得滞后并且感觉不太流畅。

演示:

我创建了一个 Snack 演示来复制该问题。您可以通过访问演示找到完整的代码并查看实际问题。

这是我的代码的简化版本:

import React, { useState, useEffect, useCallback } from 'react';
import { View, Text, FlatList, StyleSheet, ActivityIndicator, Image } from 'react-native';
import throttle from 'lodash.throttle';
import useWindowScrollStore from '../store/useWindowScrollStore';

function SocialFeed() {
  const [data, setData] = useState([]);
  const [page, setPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [hasMore, setHasMore] = useState(true);

  // Fetch data from the DummyJSON API
  const fetchData = async () => {
    if (loading) return;

    setLoading(true);
    try {
      const response = await fetch(`https://dummyjson.com/products?limit=10&skip=${(page - 1) * 10}`);
      const result = await response.json();

      if (result.products.length > 0) {
        setData((prevData) => [...prevData, ...result.products]);
        setPage(page + 1);
      } else {
        setHasMore(false);
      }
    } catch (error) {
      console.error('Error fetching data:', error);
    } finally {
      setLoading(false);
    }
  };

  // Initial fetch
  useEffect(() => {
    fetchData();
  }, []);

  const onListScroll = useCallback(
    throttle((nativeEvent) => {
      useWindowScrollStore.getState().setScrollY(nativeEvent.contentOffset.y);
    }, 100),
    []
  );

  const renderItem = ({ item }) => (
    <View style={styles.postContainer}>
      <Image source={{ uri: item.thumbnail }} style={styles.image} />
      <Text style={styles.postText}>{item.title}</Text>
      <Text style={styles.descriptionText}>{item.description}</Text>
    </View>
  );

  const renderFooter = () => {
    if (!loading) return null;
    return <ActivityIndicator size="large" color="#0000ff" />;
  };

  return (
    <View style={styles.container}>
      <FlatList
        data={data}
        renderItem={renderItem}
        keyExtractor={(item) => item.id.toString()}
        onScroll={({ nativeEvent }) => onListScroll(nativeEvent)}
        onEndReached={hasMore ? fetchData : null}
        onEndReachedThreshold={0.5}
        ListFooterComponent={renderFooter}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 10,
  },
  postContainer: {
    backgroundColor: '#f0f0f0',
    padding: 15,
    marginVertical: 8,
    borderRadius: 10,
  },
  postText: {
    fontSize: 16,
    color: '#333',
    fontWeight: 'bold',
  },
  descriptionText: {
    fontSize: 14,
    color: '#666',
    marginTop: 5,
  },
  image: {
    width: '100%',
    height: 200,
    borderRadius: 10,
    marginBottom: 10,
  },
});

export default SocialFeed;

我尝试过的:

  • 使用 lodash.throttle 限制 onScroll 事件。
  • 使用useMemo和useCallback优化列表项的渲染。
  • 通过减少重新渲染和优化状态更新来实现性能改进。

问题:

尽管进行了这些优化,当列表变大时,onScroll 事件仍然会滞后或停止正确触发。此问题会影响页眉、页脚和操作按钮切换功能,使它们对滚动事件无响应。

问题:

如何提高具有大型数据集的 FlatList 中 onScroll 事件的响应能力?我是否应该考虑其他优化技术以获得更好的性能和更流畅的滚动体验?

如有任何意见或建议,我们将不胜感激!

javascript react-native expo react-native-flatlist
1个回答
0
投票

考虑使用Animated.Flatlist和Animated.event来捕获scrollY。您的问题将会得到解决。

这是一个例子:

import * as React from 'react';
import {Animated} from 'react-native';

const List = (props) => {
    const scrollY = React.useRef(new Animated.Value(0)).current;

    const handleScroll = (event) => {
        //any other stuff if required.
    };

    const onScrollEvent = Animated.event(
        [{nativeEvent: {contentOffset: {y: scrollY}}}], //set valueY like this in zustand
        {
           useNativeDriver: true,
           listener: handleScroll,
        },
    );

    return (
        <Animated.FlatList
            scrollEventThrottle={16}
            onScroll={onScrollEvent}
            data={props.data}
            renderItem={renderItem}
            keyExtractor={(item) => item.id.toString()}
        />
    );
 };

 export default List;
© www.soinside.com 2019 - 2024. All rights reserved.