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

如何在React Native中实现API数据与SectionList的集成

如何在React Native的SectionList中集成API数据

步骤1:获取API数据

用React的useState保存原始数据、加载状态和错误信息,通过useEffect发起网络请求获取API数据:

import { useState, useEffect } from 'react';
import { View, SectionList, Text, ActivityIndicator } from 'react-native';
import RowComponent from './RowComponent'; // 导入你的自定义行组件

const TransactionList = () => {
  const [rawData, setRawData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchTransactions = async () => {
      try {
        const response = await fetch('你的API接口地址');
        if (!response.ok) throw new Error('请求失败');
        const data = await response.json();
        setRawData(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchTransactions();
  }, []);

  // 后续逻辑写在此处
};

步骤2:转换API数据为SectionList所需格式

需要将API返回的扁平数据按日期分组(今日、昨日、历史日期),并映射为SectionList要求的结构:

// 辅助函数:判断交易日期所属分组
const getDateGroup = (dateString) => {
  const today = new Date();
  const transactionDate = new Date(dateString);
  
  // 重置时间部分,仅比较日期
  const normalizeDate = (date) => {
    const d = new Date(date);
    d.setHours(0, 0, 0, 0);
    return d;
  };

  const normalizedToday = normalizeDate(today);
  const normalizedTransactionDate = normalizeDate(transactionDate);
  const oneDay = 24 * 60 * 60 * 1000;

  if (normalizedTransactionDate.getTime() === normalizedToday.getTime()) {
    return { title: 'Today', key: 'today' };
  } else if (normalizedTransactionDate.getTime() === normalizedToday.getTime() - oneDay) {
    return { title: 'Yesterday', key: 'yesterday' };
  } else {
    // 格式化历史日期为「DD MMM YYYY」格式
    const formattedDate = transactionDate.toLocaleDateString('en-GB', {
      day: '2-digit',
      month: 'short',
      year: 'numeric'
    }).toUpperCase();
    return { title: formattedDate, key: formattedDate.toLowerCase() };
  }
};

// 核心转换函数:将API数据转为SectionList结构
const transformApiData = (data) => {
  // 按日期分组
  const grouped = data.reduce((acc, item) => {
    const { title: groupTitle, key: groupKey } = getDateGroup(item.createdAt);
    if (!acc[groupKey]) {
      acc[groupKey] = {
        title: groupTitle,
        key: groupKey,
        data: []
      };
    }
    // 映射API字段到RowComponent所需结构
    acc[groupKey].data.push({
      id: item.id, // 用于唯一key
      iconUrl: '', // 可根据transactionType/status设置对应图标地址
      primaryKey: item.title || item.transactionType, // 主标题
      primaryLabel: item.quantity, // 交易数量
      secondaryKey: new Date(item.createdAt).toLocaleTimeString('en-GB', {
        hour: '2-digit',
        minute: '2-digit'
      }), // 交易时间
      secondaryLabel: item.token // 代币类型
    });
    return acc;
  }, {});

  // 排序分组:今日→昨日→历史日期从新到旧
  return Object.values(grouped).sort((a, b) => {
    if (a.key === 'today') return -1;
    if (b.key === 'today') return 1;
    if (a.key === 'yesterday') return -1;
    if (b.key === 'yesterday') return 1;
    return new Date(b.title) - new Date(a.title);
  });
};

// 在组件中生成SectionList所需的sections
const sections = transformApiData(rawData);

步骤3:调整SectionList渲染逻辑

替换静态数据为转换后的sections,并使用API返回的id作为唯一key:

const handleRenderSectionItem = ({ item }) => {
  const { iconUrl, primaryKey, primaryLabel, secondaryKey, secondaryLabel } = item;

  return (
    <RowComponent 
      onPress={() => { /* 可添加点击跳转逻辑 */ }}
      iconUri={iconUrl}
      primaryKey={primaryKey}
      primaryLabel={primaryLabel}
      secondaryKey={secondaryKey}
      secondaryLabel={secondaryLabel}
    />
  );
};

const handleRenderSectionHeader = ({ section: { title }}) => (
  <Text style={{ marginVertical: 15, fontSize: 16, fontWeight: '600' }}>
    {title}
  </Text>
);

// 加载/错误状态处理
if (loading) return <ActivityIndicator size="large" color="#000" style={{ flex: 1, justifyContent: 'center' }} />;
if (error) return <Text style={{ flex: 1, textAlign: 'center', padding: 20 }}>加载失败:{error}</Text>;

// 最终渲染
return (
  <View style={{ flex: 1, paddingHorizontal: 12 }}>
    <SectionList 
      sections={sections}
      keyExtractor={(item) => item.id.toString()}
      renderItem={handleRenderSectionItem}
      renderSectionHeader={handleRenderSectionHeader}
      stickySectionHeadersEnabled={false}
      contentContainerStyle={{ paddingVertical: 10 }}
    />
  </View>
);

完整组件代码

整合所有逻辑后的完整实现:

import { useState, useEffect } from 'react';
import { View, SectionList, Text, ActivityIndicator } from 'react-native';
import RowComponent from './RowComponent';

const TransactionList = () => {
  const [rawData, setRawData] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchTransactions = async () => {
      try {
        const response = await fetch('你的API接口地址');
        if (!response.ok) throw new Error('请求失败');
        const data = await response.json();
        setRawData(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchTransactions();
  }, []);

  const getDateGroup = (dateString) => {
    const today = new Date();
    const transactionDate = new Date(dateString);
    
    const normalizeDate = (date) => {
      const d = new Date(date);
      d.setHours(0, 0, 0, 0);
      return d;
    };

    const normalizedToday = normalizeDate(today);
    const normalizedTransactionDate = normalizeDate(transactionDate);
    const oneDay = 24 * 60 * 60 * 1000;

    if (normalizedTransactionDate.getTime() === normalizedToday.getTime()) {
      return { title: 'Today', key: 'today' };
    } else if (normalizedTransactionDate.getTime() === normalizedToday.getTime() - oneDay) {
      return { title: 'Yesterday', key: 'yesterday' };
    } else {
      const formattedDate = transactionDate.toLocaleDateString('en-GB', {
        day: '2-digit',
        month: 'short',
        year: 'numeric'
      }).toUpperCase();
      return { title: formattedDate, key: formattedDate.toLowerCase() };
    }
  };

  const transformApiData = (data) => {
    const grouped = data.reduce((acc, item) => {
      const { title: groupTitle, key: groupKey } = getDateGroup(item.createdAt);
      if (!acc[groupKey]) {
        acc[groupKey] = {
          title: groupTitle,
          key: groupKey,
          data: []
        };
      }
      acc[groupKey].data.push({
        id: item.id,
        iconUrl: '',
        primaryKey: item.title || item.transactionType,
        primaryLabel: item.quantity,
        secondaryKey: new Date(item.createdAt).toLocaleTimeString('en-GB', {
          hour: '2-digit',
          minute: '2-digit'
        }),
        secondaryLabel: item.token
      });
      return acc;
    }, {});

    return Object.values(grouped).sort((a, b) => {
      if (a.key === 'today') return -1;
      if (b.key === 'today') return 1;
      if (a.key === 'yesterday') return -1;
      if (b.key === 'yesterday') return 1;
      return new Date(b.title) - new Date(a.title);
    });
  };

  const sections = transformApiData(rawData);

  const handleRenderSectionItem = ({ item }) => {
    const { iconUrl, primaryKey, primaryLabel, secondaryKey, secondaryLabel } = item;

    return (
      <RowComponent 
        onPress={() => {}}
        iconUri={iconUrl}
        primaryKey={primaryKey}
        primaryLabel={primaryLabel}
        secondaryKey={secondaryKey}
        secondaryLabel={secondaryLabel}
      />
    );
  };

  const handleRenderSectionHeader = ({ section: { title }}) => (
    <Text style={{ marginVertical: 15, fontSize: 16, fontWeight: '600' }}>
      {title}
    </Text>
  );

  if (loading) return <ActivityIndicator size="large" color="#000" style={{ flex: 1, justifyContent: 'center' }} />;
  if (error) return <Text style={{ flex: 1, textAlign: 'center', padding: 20 }}>加载失败:{error}</Text>;

  return (
    <View style={{ flex: 1, paddingHorizontal: 12 }}>
      <SectionList 
        sections={sections}
        keyExtractor={(item) => item.id.toString()}
        renderItem={handleRenderSectionItem}
        renderSectionHeader={handleRenderSectionHeader}
        stickySectionHeadersEnabled={false}
        contentContainerStyle={{ paddingVertical: 10 }}
      />
    </View>
  );
};

export default TransactionList;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 12:39:51