You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在FlatList底部显示ActivityIndicator?解决加载指示器不显示及API重复调用问题

问题分析与解决方案

你遇到的两个问题:加载指示器不显示、loadShops重复调用,主要是分页逻辑设计和状态控制的问题,下面逐个拆解并给出修复方案:

一、为什么ActivityIndicator不显示?

你的代码中isLoading的状态更新逻辑本身没问题,但可能存在以下潜在原因:

  1. ListFooterComponent使用了函数返回的形式,虽然FlatList支持这种写法,但有时候会因为状态捕获延迟导致不渲染;
  2. 当shopLimit超过30时,useEffect会跳过loadShops调用,此时如果之前的isLoading被设为false,就不会显示指示器,若加载过程中触发这个条件也会导致指示器提前消失。

二、为什么loadShops会被重复调用?

核心原因是onEndReached被多次触发:

  • 你设置的onEndReachedThreshold={0.01}过于敏感,意味着滚动到距离底部几乎0的位置就会触发回调;
  • 滚动过程中scrollEventThrottle={150}会频繁触发滚动事件,可能导致onEndReached被多次调用,进而多次执行setShopLimit,最终触发多次useEffect和loadShops;
  • 没有在加载过程中阻止新请求,即当isLoading为true时,依然允许触发新的分页请求。

修复后的完整代码

我重构了分页逻辑(改用固定limit+递增skip的常规分页方案,比动态增加limit更合理),同时解决了上述两个问题:

const dispatch = useDispatch();
const shops = useSelector((state) => state.shops.availableShops);
const [limit] = useState(10); // 固定每页加载10条
const [skip, setSkip] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [hasMore, setHasMore] = useState(true); // 标记是否还有更多数据

const loadShops = async () => {
  if (isLoading || !hasMore) return; // 正在加载或无更多数据时直接返回
  setIsLoading(true);
  try {
    await dispatch(shopsActions.getShopsList(skip, limit));
    // 用skip + limit <=30模拟你的原逻辑(最多加载30条)
    // 实际项目中可根据API返回的总数据量判断是否还有更多
    if (skip + limit >= 30) {
      setHasMore(false);
    }
  } catch (error) {
    console.error("加载店铺失败:", error);
    // 可在此添加错误提示逻辑
  } finally {
    setIsLoading(false); // 无论成功失败,都要结束加载状态
  }
};

useEffect(() => {
  loadShops();
}, [skip]); // 依赖改为skip,每次skip变化时加载新数据

return (
  <View style={styles.container}>
    <SearchBarCmp 
      containerStyle={styles.containerStyle} 
      inputContainerStyle={styles.inputContainerStyle} 
      placeholder="Search by shop name or location..." 
    />
    <FlatList
      data={shops}
      renderItem={(shop) => (
        <ShopItem
          imageUrl={shop.item.url}
          shopName={shop.item.name}
          pickupHour={shop.item.pickup_hour}
          originalPrice={shop.item.original_price}
          discountedPrice={shop.item.discounted_price}
        />
      )}
      keyExtractor={(item, index) => index.toString()}
      onEndReached={() => {
        // 只有在非加载状态且还有更多数据时,才更新skip
        if (!isLoading && hasMore) {
          setSkip(prev => prev + limit);
        }
      }}
      onEndReachedThreshold={0.1} // 调整为0.1,避免过于敏感触发多次
      scrollEventThrottle={300} // 适当增大节流时间,减少无效触发
      ListFooterComponent={isLoading ? (
        <View style={styles.footerContainer}>
          <ActivityIndicator size="large" color="#0000ff" />
        </View>
      ) : null}
    />
  </View>
);

关键修改点说明

  1. 分页逻辑重构:

    • 固定limit为10,用skip递增实现分页,符合绝大多数API的分页设计;
    • 添加hasMore状态,标记是否还有更多数据可加载,避免无效请求。
  2. 阻止重复调用:

    • 在loadShops开头增加状态判断,正在加载或无更多数据时直接返回;
    • 在onEndReached里也增加状态校验,仅在非加载且有更多数据时更新skip;
    • 调整onEndReachedThreshold和scrollEventThrottle参数,减少不必要的触发。
  3. 加载指示器修复:

    • 将ListFooterComponent改为直接条件渲染组件,避免函数形式可能带来的状态捕获问题;
    • 使用finally块确保isLoading一定会被设为false,避免加载状态卡死。

内容的提问来源于stack exchange,提问作者AndroidDude123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 09:47:38