且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

按键对React Native SectionList进行排序和分组

更新时间:2023-11-26 10:06:52

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

 // 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> 

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..

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();
}

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 it currently looks:

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.

Any help would be amazing!

FULL COMPONENT:

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>
  );
  }
}

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>