按键排序和分组 React Native SectionList

2022-01-23 00:00:00 arrays sorting javascript react-native expo

我有一个 SectionList,我用来自 firebase 的数据填充.该列表显示按日期划分的事件信息,当前月份显示为 THIS MONTH,其他日期使用它们的简写值 JANFEB等等.

I have a SectionList which I populate with data from firebase. The list shows event info sectioned by date, with the current month visible as THIS MONTH and the other dates using their shorthand value of JAN, FEB etc..

我得到了很好的数据并且可以很好地显示它,但我不知道如何按日期对数据数组进行分组.仅供参考,日期作为日期字符串2019-01-05"保存在数据库中,我使用 moment 用这种方法格式化它们:

I get the data fine and can display it fine but I can't figure out how to group the data array by the dates. FYI the dates are saved in the db as date strings '2019-01-05' and i use moment to format them with this method:

 formatDateToMonth(date) {
  let fullDate = moment(date);
  fullDate.month();
  const month = fullDate.format('MMM');
  return month.toUpperCase();
}

返回 JAN.我运行一个简单的 if 语句来检查 title 的值与我用 moment 格式化的当前月份,如果匹配,我将 title 的显示名称更改为 THIS MONTH.

Which returns JAN. I run a simple if statement to check the value of title against the current month which I format with moment and if its a match I change the display name of title to THIS MONTH.

数据被推送到一个数组中,如下所示:

The data is pushed to an array like so:

listData.push({
  data: [
    {
      id: event,
      eventName: eventObj.eventName,
      location: eventObj.location,
      description: eventObj.description,
      creatorsName: eventObj.creatorsName,
      date: eventObj.date,
      discipline: eventObj.discipline
    }
  ],
  title
});

标题是月份名称和我需要对其进行排序和分组的键.

With the title being the month name and the key that I need to sort and group this by.

这是它目前的样子:

这是我希望它的外观:(使用虚拟数据正确呈现)

This is how I would like it to look: (used dummy data to present it correctly)

我希望同一个月内的每个事件都在同一部分下.

I would like each event that is in the same month to be under the same section.

任何帮助都会很棒!

完整组件:

import React from 'react';
import {View, StyleSheet, SectionList, TouchableOpacity} from 'react-native';
import { Container, Content , Text, Icon, Spinner } from 'native-base';
import Collapsible from 'react-native-collapsible';
import {f, auth, database} from '../../config/config';
import _ from 'lodash';

import CustomIcon from '../utilities/CustomIcon';

import Month from './Month';
import moment from 'moment';
import Day from './Day';

let today = moment();
let now = today.format("YYYY-MM-DD");
let getCurrentMonth = today.month('MMM');

const monthOrder = [
  'JAN',
  'FEB',
  'MAR',
  'APR',
  'MAY',
  'JUN',
  'JUL',
  'AUG',
  'SEPT',
  'OCT',
  'NOV',
  'DEC'
];

class SectionListItem extends React.Component {

  state = {
    descriptionCollapsed: true
  };

  toggleDescription = () => {
    this.setState({ descriptionCollapsed: !this.state.descriptionCollapsed
    });
  };

  render() {
    let fullDay = moment(this.props.item.date);
    fullDay.day();
    const day = fullDay.format('DD')
    return (
      <View style={styles.sectionListItemContainer}>
      <View style={styles.eventInfoContainer}>
      <View>
      <Day day={day}/>
    </View>
    <TouchableOpacity onPress={this.toggleDescription}>
      <View style={styles.info}>
      <Text style={styles.eventName}>{this.props.item.eventName.toUpperCase()}</Text>
      <Text style={styles.creatorsName}>Coached by {this.props.item.creatorsName}</Text>
    <Text style={styles.location}>{this.props.item.location}</Text>
      </View>
      </TouchableOpacity>
      </View>
      <Collapsible collapsed={this.state.descriptionCollapsed}>
      <EventDescription description={this.props.item.description} discipline={this.props.item.discipline}/>
    </Collapsible>
    </View>
  );
  }
}

class EventDescription extends React.Component {

  render() {
    return (
      <View style={styles.descriptionDropdown}>
      <View >
      <Icon name='arrow-up' type="SimpleLineIcons" style={styles.upArrow} />
    </View>
    <View style={styles.descriptionContainer}>
      <Text style={styles.description}>{this.props.description}</Text>
      </View>
      <View style={styles.iconContainer}>
      {!!this.props.discipline ? (this.props.discipline.map((item, index) => {
      return <View key={index} style={styles.iconMargin}><CustomIcon name={item} size={20} style={styles.iconStyle}/></View>
    })) : null}
  </View>
    </View>
  );
  }
}

class SectionHeader extends React.Component {
  render() {
    return (
      <View style={styles.monthHeader}>
      <Month month={this.props.section.title}/>
    </View>
  );
  }
}

export default class EventList extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      filterCollapsed: true,
      listData: [],
      loading: true,
      refreshing: false
    }
  }

  componentDidMount() {
    this.loadEvents();
  }

  onRefresh = () => {
    this.setState({refreshing: true})
    this.loadEvents();
  }

  formatDateToMonth(date) {
    let fullDate = moment(date);
    fullDate.month();
    const month = fullDate.format('MMM');
    return month.toUpperCase();
  }

  getDateMatches = (date) => {
    return database
      .ref('events')
      .orderByChild('date')
      .startAt(date)
      .once("value")
      .then((snapshot) => {
        let matches = [];
        snapshot.forEach((child) => {
          let val = child.val();
          const valMonth = this.formatDateToMonth(val.date);
          const dateMonth = this.formatDateToMonth(date);
          if (dateMonth === valMonth) {
            matches.push(val);
          }
        });
        return matches;
      });
  }

  loadEvents = () => {
    this.setState({listData: []});
    const that = this;

    database.ref('events').once('value').then((snapshot) => {
      const exists = (snapshot.val() !== null);
      if (exists) {
        data = snapshot.val();
      }
      const listData = that.state.listData;

      let monthData = {title: '', data: []};

      const thisMonth = { title: 'THIS MONTH', data: []};
      const otherMonth = { title: '', data: []};

      for(var event in data) {
        const eventObj = data[event];

        const month = this.formatDateToMonth(eventObj.date);
        let title;

        getCurrentMonth.month();
        const currentMonth = getCurrentMonth.format('MMM');


        if (month === currentMonth.toUpperCase()) {
          title = 'THIS MONTH';
        } else {
          title = month;
        }

        listData.push(
          {
            data: [
              {
                id: event,
                eventName: eventObj.eventName,
                location: eventObj.location,
                description: eventObj.description,
                creatorsName: eventObj.creatorsName,
                date: eventObj.date,
                discipline: eventObj.discipline,
              }
            ],
            title
          }
        );

        // listData.sort((a, b) => {
        //     return (a.title - b.title) || (monthOrder.indexOf(a.title) - monthOrder.indexOf(b.title))
        // });



        that.setState({loading: false, refreshing: false});

      }

    }).catch(error => console.log('error: ', error));
  }


  toggleFilter = () => {
    this.setState({ filterCollapsed: !this.state.filterCollapsed });
  };

  render() {

    if (!!this.state.loading) {
      return (
        <View style={styles.spinner}>
        <Spinner color="#81e6fc"/>
        </View>
    )
    }

    return (
      <Container style={styles.container}>
      <TouchableOpacity style={styles.filterTextContainer} onPress={this.toggleFilter}>
      <Text style={styles.filterText}>FILTER</Text>
      </TouchableOpacity>
      <Collapsible collapsed={this.state.filterCollapsed}>
      <CustomIcon name="Rate" size={50} style={styles.iconStyle}/>
    </Collapsible>
    <Content contentContainerStyle={styles.list}
    onRefresh={this.onRefresh}
    refreshing={this.state.refreshing}
      >
      <SectionList
    renderItem={({item, index}) => {
      return <SectionListItem item={item} index={index}/>
    }}
    renderSectionHeader={({section}) => {
      return <SectionHeader section={section}/>
    }}
    sections={dummyData}
    keyExtractor={(item, index) => item + index}
  >
  </SectionList>
    </Content>
    </Container>
  );
  }
}

推荐答案

我假设您不关心分组中的年份,因为在这种情况下,2018-01 和 2019-01 都将是 JAN.但无论如何看看下面的例子,我试着注释代码中的每一步:

I assume you do not care about years in your groupping, because in that case 2018-01 and 2019-01 would both be JAN. But anyway take a look at example below, I tried to comment every step in code:

// define your flat list of events
const events = [
	{name: 'dummy one', date: '2019-01-05'},
	{name: 'dummy some', date: '2019-02-04'},
	{name: 'dummy another', date: '2019-01-07'},
	{name: 'dummy really dummy', date: '2019-04-05'},
	{name: 'dummy and funny', date: '2019-06-05'},
	{name: 'dummy not funny', date: '2019-06-22'}
].sort((a, b) => a.date < b.date ? -1 : 1)
// remember to sort them early so you will not have to worry about it anytime later on

// extract distinct names from available events, we are using Set constructor to make sure any key won't be doubled and also that there won't be any empty month
const groupNames = Array.from(new Set(events.map(k => k.date.split('-')[1]))) // split date by hyphen and return only month part

// define place for groups
let groups = {}

// create groups containers from names
groupNames.forEach(k => {
	groups[k] = []
})

// iterate by events and attach every to given group based on its month
events.forEach(k => {
	const month = k.date.split('-')[1]
  groups[month].push(k)
})

// container for demo purposes
let resultHTML = ''

// iterate by groups, if you are unsure about 'for in' loop, use groupNames for that part, and then iterate by events in every group
for(let key in groups) {
	resultHTML += `<h2>${key}</h2>`
  for (let event of groups[key]) {
  	resultHTML += `<p>${event.date} &mdash; ${event.name}</p>`
  }
}

// display results for demo purposes
document.querySelector('.result').innerHTML = resultHTML

<div class="result"></div>

相关文章