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
相关产品推荐
相关产品推荐

