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

React Native中实现列表双条件排序及分组自定义UI方案

React Native 赛事列表多规则排序+分组UI实现方案

你之前使用sortable list组件未成功的核心原因是组件选型错误:这类组件的核心能力是支持用户拖拽手动调整顺序,不会按业务规则自动完成多维度排序,实现该需求不需要引入额外的排序类组件,使用React Native内置的SectionList配合纯JS排序逻辑即可落地。


第一步:实现多维度排序逻辑

严格按照三层排序优先级编写排序函数,排序时从高优先级到低优先级依次判断,前序维度结果相同时再进入下一层维度判断:

  • 第一优先级:比赛日期,今日>明日>其他日期
  • 第二优先级:赛事重要等级,高优先级>低优先级
  • 第三优先级:同日期同赛事下,开赛时间早>开赛时间晚
// 计算日期维度排序权重
const getDayPriority = (matchTimestamp) => {
  const now = new Date();
  const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
  const tomorrowStart = todayStart + 24 * 60 * 60 * 1000;
  const dayAfterStart = tomorrowStart + 24 * 60 * 60 * 1000;

  if (matchTimestamp >= todayStart && matchTimestamp < tomorrowStart) return 0;
  if (matchTimestamp >= tomorrowStart && matchTimestamp < dayAfterStart) return 1;
  return 2;
};

// 计算赛事等级维度排序权重,可根据后端返回的等级字段自行调整映射关系
const getLevelPriority = (tournamentLevel) => {
  const levelWeightMap = {
    high: 0,
    medium: 1,
    low: 2
  };
  return levelWeightMap[tournamentLevel] ?? 99;
};

// 核心排序函数
const sortMatchList = (rawList) => {
  // 浅拷贝避免修改原数组
  return [...rawList].sort((a, b) => {
    // 第一维度:日期优先级判断
    const dayDiff = getDayPriority(a.matchTime) - getDayPriority(b.matchTime);
    if (dayDiff !== 0) return dayDiff;
    // 第二维度:赛事等级优先级判断
    const levelDiff = getLevelPriority(a.tournamentLevel) - getLevelPriority(b.tournamentLevel);
    if (levelDiff !== 0) return levelDiff;
    // 第三维度:开赛时间升序判断
    return a.matchTime - b.matchTime;
  });
};

第二步:将排序后的一维列表转换为分组结构

排序完成后需要把平铺的赛事数据转换为SectionList支持的分组结构,为后续渲染三类行组件做准备:

const buildGroupSections = (sortedList) => {
  const sections = [];
  let lastRenderedDay = null;

  sortedList.forEach(match => {
    const matchDate = new Date(match.matchTime);
    const dayKey = `${matchDate.getFullYear()}-${matchDate.getMonth() + 1}-${matchDate.getDate()}`;
    const sectionKey = `${dayKey}-${match.tournamentId}`;

    // 查找当前日期+赛事对应的分组是否已存在
    let currentSection = sections.find(item => item.key === sectionKey);
    if (!currentSection) {
      currentSection = {
        key: sectionKey,
        // 同日期下仅第一个分组显示日期头,避免重复
        dayLabel: lastRenderedDay === dayKey ? null : (getDayPriority(match.matchTime) === 0 ? '今日赛事' : '明日赛事'),
        tournamentInfo: {
          id: match.tournamentId,
          name: match.tournamentName,
          level: match.tournamentLevel
        },
        data: []
      };
      sections.push(currentSection);
      lastRenderedDay = dayKey;
    }
    currentSection.data.push(match);
  });

  return sections;
};

第三步:自定义三类行组件,渲染最终列表

分别实现日期分组行、赛事分组行、单场赛事行三类组件,传入SectionList完成渲染:

import { SectionList, View, Text, StyleSheet } from 'react-native';
import { useMemo } from 'react';

// 日期分组行组件
const DateSectionRow = ({ label }) => {
  return (
    <View style={styles.dateRow}>
      <Text style={styles.dateText}>{label}</Text>
    </View>
  );
};

// 赛事分组行组件
const TournamentSectionRow = ({ tournamentInfo }) => {
  return (
    <View style={styles.tournamentRow}>
      <Text style={styles.tournamentName}>{tournamentInfo.name}</Text>
      {tournamentInfo.level === 'high' && <Text style={styles.highLevelTag}>高优先级</Text>}
    </View>
  );
};

// 单场赛事行组件
const MatchItemRow = ({ match }) => {
  const startTime = new Date(match.matchTime).toTimeString().slice(0, 5);
  return (
    <View style={styles.matchRow}>
      <Text style={styles.playerText}>{match.player1} VS {match.player2}</Text>
      <Text style={styles.timeText}>{startTime}</Text>
    </View>
  );
};

// 列表容器组件
const MatchList = ({ rawData }) => {
  // 缓存排序和分组计算结果,避免重复渲染时重复计算
  const sections = useMemo(() => {
    const sortedList = sortMatchList(rawData);
    return buildGroupSections(sortedList);
  }, [rawData]);

  const renderSectionHeader = ({ section }) => (
    <View>
      {section.dayLabel && <DateSectionRow label={section.dayLabel} />}
      <TournamentSectionRow tournamentInfo={section.tournamentInfo} />
    </View>
  );

  return (
    <SectionList
      sections={sections}
      keyExtractor={item => item.matchId}
      renderSectionHeader={renderSectionHeader}
      renderItem={({ item }) => <MatchItemRow match={item} />}
      stickySectionHeadersEnabled={false}
    />
  );
};

const styles = StyleSheet.create({
  dateRow: { padding: 12, backgroundColor: '#f5f5f5' },
  dateText: { fontSize: 16, fontWeight: 'bold' },
  tournamentRow: { padding: 10, flexDirection: 'row', alignItems: 'center', borderBottomWidth: 1, borderColor: '#eee' },
  tournamentName: { fontSize: 15, color: '#333' },
  highLevelTag: { fontSize: 12, color: '#ff4d4f', marginLeft: 8 },
  matchRow: { padding: 12, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
  playerText: { fontSize: 14 },
  timeText: { fontSize: 14, color: '#666' }
});

扩展说明

  • 如果后续需要新增排序维度,只需要在sortMatchList函数中新增对应维度的权重判断逻辑即可,不需要修改UI层代码
  • 如果需要支持列表头吸顶效果,只需要将SectionList的stickySectionHeadersEnabled属性设为true
  • 单场赛事行、分组行的样式可以完全按照设计需求自定义,不受组件限制

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 04:39:20