按键对React Native SectionList进行排序和分组 [英] Sort and Group React Native SectionList by Key

查看:61
本文介绍了按键对React Native SectionList进行排序和分组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个SectionList,其中填充了来自Firebase的数据.该列表显示按日期划分的事件信息,当前月份显示为THIS MONTH,其他日期则使用其速记值JANFEB等.

我得到的数据很好,并且可以很好地显示,但是我不知道如何按日期对数据数组进行分组.仅供参考,日期以日期字符串'2019-01-05'的形式保存在数据库中,我使用一下矩通过以下方法对其进行格式化:

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

哪个返回JAN.我运行一个简单的if语句来检查当前月份的title值,并用时刻对其进行格式化,如果匹配,则将title的显示名称更改为THIS 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
});

标题是月份名称,我需要对它进行排序和分组.

这是当前的外观:

这就是我希望的样子:(使用伪数据正确显示了它)

我希望同一月份的每个活动都在同一部分下.

任何帮助都将是惊人的!

完整组件:

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.但是无论如何,请看下面的示例,我试图注释代码中的每个步骤:

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

这篇关于按键对React Native SectionList进行排序和分组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆