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

React Native中ScrollView底部加载指示器名称咨询

React Native 滚动触底加载更多与底部指示器实现

核心说明

React Native 的 ScrollView 本身没有内置专属的“底部加载指示器”组件,但可以通过监听滚动事件判断是否到达底部,再自定义加载提示组件。更推荐使用专为列表场景优化的 FlatList(它继承自 ScrollView),FlatList 提供了现成的触底监听属性,能更便捷地实现加载逻辑,同时可通过 ListFooterComponent 设置底部加载指示器。

方案一:使用 FlatList 实现(推荐)

FlatList 自带 onEndReached(触底触发回调)、onEndReachedThreshold(距离底部多远触发,取值0-1,比如0.1表示距离底部10%高度时触发),以及 ListFooterComponent(设置底部加载提示)。

修改你的示例代码如下:

import React, { Component } from "react";
import { Text, Button, View, StyleSheet, FlatList, ActivityIndicator } from "react-native";

class App extends Component {
  state = {
    items: [
      { item: "Item 1", price: "10", id: 1 },
      { item: "Item 2", price: "20", id: 2 },
      { item: "Item 3", price: "30", id: 3 },
      { item: "Item 4", price: "40", id: 4 },
      { item: "Item 5", price: "50", id: 5 },
      { item: "Item 6", price: "60", id: 6 },
      { item: "Item 7", price: "70", id: 7 },
      { item: "Item 8", price: "80", id: 8 },
      { item: "Item 9", price: "90", id: 9 },
      { item: "Item 10", price: "100", id: 10 },
      { item: "Item 11", price: "110", id: 11 },
      { item: "Item 12", price: "120", id: 12 },
      { item: "Item 13", price: "130", id: 13 },
      { item: "Item 14", price: "140", id: 14 },
      { item: "Item 15", price: "150", id: 15 },
    ],
    isLoading: false,
    page: 1,
  };

  // 模拟加载更多数据
  loadMoreData = () => {
    if (this.state.isLoading) return; // 防止重复触发
    this.setState({ isLoading: true });
    // 模拟异步请求,实际项目替换为接口调用
    setTimeout(() => {
      const newItems = Array.from({ length: 5 }, (_, i) => {
        const id = this.state.items.length + i + 1;
        return { item: `Item ${id}`, price: `${id * 10}`, id };
      });
      this.setState(prevState => ({
        items: [...prevState.items, ...newItems],
        isLoading: false,
        page: prevState.page + 1,
      }));
    }, 1500);
  };

  renderItem = ({ item }) => (
    <View style={styles.summary}>
      <Text style={styles.summaryText}>
        {item.item} <Text style={styles.amount}>${item.price}</Text>
      </Text>
      <Button title="Order" color="#FFC107" onPress={() => { }} />
    </View>
  );

  renderFooter = () => {
    if (!this.state.isLoading) return null;
    // 自定义底部加载指示器,这里用官方ActivityIndicator组件
    return (
      <View style={styles.loaderContainer}>
        <ActivityIndicator size="large" color="#FFC107" />
        <Text style={styles.loaderText}>加载中...</Text>
      </View>
    );
  };

  render() {
    return (
      <View style={styles.screen}>
        <FlatList
          data={this.state.items}
          renderItem={this.renderItem}
          keyExtractor={item => item.id.toString()}
          onEndReached={this.loadMoreData}
          onEndReachedThreshold={0.1}
          ListFooterComponent={this.renderFooter}
        />
      </View>
    );
  }
}

// 样式定义
const styles = StyleSheet.create({
  screen: {
    margin: 20,
  },
  summary: {
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    marginBottom: 20,
    padding: 10,
    shadowColor: "black",
    shadowOpacity: 0.26,
    shadowOffset: { width: 0, height: 2 },
    shadowRadius: 8,
    elevation: 5,
    borderRadius: 10,
    backgroundColor: "white",
  },
  summaryText: {
    fontFamily: "openSansBold",
    fontSize: 18,
  },
  amount: {
    color: "#C2185B",
  },
  loaderContainer: {
    paddingVertical: 20,
    alignItems: "center",
  },
  loaderText: {
    marginTop: 8,
    fontSize: 16,
    color: "#666",
  },
});

export default App;

方案二:使用 ScrollView 实现

如果必须用ScrollView,需通过onScroll事件监听滚动位置,手动计算是否到达底部:

import React, { Component } from "react";
import { Text, Button, View, StyleSheet, ScrollView, ActivityIndicator } from "react-native";

class App extends Component {
  state = {
    items: [
      { item: "Item 1", price: "10", id: 1 },
      { item: "Item 2", price: "20", id: 2 },
      { item: "Item 3", price: "30", id: 3 },
      { item: "Item 4", price: "40", id: 4 },
      { item: "Item 5", price: "50", id: 5 },
      { item: "Item 6", price: "60", id: 6 },
      { item: "Item 7", price: "70", id: 7 },
      { item: "Item 8", price: "80", id: 8 },
      { item: "Item 9", price: "90", id: 9 },
      { item: "Item 10", price: "100", id: 10 },
      { item: "Item 11", price: "110", id: 11 },
      { item: "Item 12", price: "120", id: 12 },
      { item: "Item 13", price: "130", id: 13 },
      { item: "Item 14", price: "140", id: 14 },
      { item: "Item 15", price: "150", id: 15 },
    ],
    isLoading: false,
  };

  handleScroll = (event) => {
    const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent;
    // 计算是否到达底部,预留20px偏移避免精度问题
    const isReachedBottom = contentOffset.y + layoutMeasurement.height >= contentSize.height - 20;
    if (isReachedBottom && !this.state.isLoading) {
      this.loadMoreData();
    }
  };

  loadMoreData = () => {
    this.setState({ isLoading: true });
    setTimeout(() => {
      const newItems = Array.from({ length: 5 }, (_, i) => {
        const id = this.state.items.length + i + 1;
        return { item: `Item ${id}`, price: `${id * 10}`, id };
      });
      this.setState(prevState => ({
        items: [...prevState.items, ...newItems],
        isLoading: false,
      }));
    }, 1500);
  };

  render() {
    return (
      <View style={styles.screen}>
        <ScrollView
          onScroll={this.handleScroll}
          scrollEventThrottle={16} // 每秒触发60次,保证监听流畅
        >
          {this.state.items.map((item) => (
            <View key={item.id}>
              <View style={styles.summary}>
                <Text style={styles.summaryText}>
                  {item.item} <Text style={styles.amount}>${item.price}</Text>
                </Text>
                <Button title="Order" color="#FFC107" onPress={() => { }} />
              </View>
            </View>
          ))}
          {/* 底部加载指示器 */}
          {this.state.isLoading && (
            <View style={styles.loaderContainer}>
              <ActivityIndicator size="large" color="#FFC107" />
              <Text style={styles.loaderText}>加载中...</Text>
            </View>
          )}
        </ScrollView>
      </View>
    );
  }
}

// 样式定义
const styles = StyleSheet.create({
  screen: {
    margin: 20,
  },
  summary: {
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "space-between",
    marginBottom: 20,
    padding: 10,
    shadowColor: "black",
    shadowOpacity: 0.26,
    shadowOffset: { width: 0, height: 2 },
    shadowRadius: 8,
    elevation: 5,
    borderRadius: 10,
    backgroundColor: "white",
  },
  summaryText: {
    fontFamily: "openSansBold",
    fontSize: 18,
  },
  amount: {
    color: "#C2185B",
  },
  loaderContainer: {
    paddingVertical: 20,
    alignItems: "center",
  },
  loaderText: {
    marginTop: 8,
    fontSize: 16,
    color: "#666",
  },
});

export default App;

关键提示

  • 官方内置的加载指示器组件是 ActivityIndicator,通常作为底部加载提示的核心UI,也可以自定义文字+指示器的组合样式。
  • 使用FlatList时,onEndReachedThreshold的值可按需调整,0表示刚好滚动到底部时触发,0.1表示还有10%屏幕高度的内容未滚动时触发。
  • 两种方案都需要添加isLoading状态,防止重复触发加载请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 12:30:58