【问题标题】:How can I show days by group like Whatsapp chat screen?如何按组显示日期,例如 Whatsapp 聊天屏幕?
【发布时间】:2020-07-28 22:39:44
【问题描述】:

我怎样才能做一个类似于 Whatsapp 聊天屏幕中的日期系统?

如您所见,消息按日期分组,我的意思是它们按日期分隔。

这是我找到的更好解释的屏幕截图:

我在 FlatList 中执行此操作,同时一一呈现消息。

这就是我所做的

        let previousDate = "";
    if (index > 0) {
        previousDate = moment(this.state.messages[index - 1].created_at).format(
            "L"
        );
    } else {
        previousDate = moment(this.state.messages.created_at).format("L");
    }

    let currentDate = moment(item.created_at).format("L");

所以,我为 FlatList 的 renderItem 属性创建了一个功能组件,所以 itemindex 来自 FlatList 的实际数据。

我在这里要做的是,基本上抓取当前渲染项目的created_at 并将其与前一个项目的created_at 进行比较,为此,我使用存储在状态。但不幸的是,当 FlatList 呈现索引号为0 的第一个项目时,在状态的原始数据中没有前一个元素可以比较,这就是为什么我检查是否大于 0 并从上一个索引项目中获取日期。在 Else 情况下,这意味着在渲染第一个项目时,不要查找前一个项目,而只需获取 created_at

下面我检查currentDatepreviousDates 是否不相同,渲染一个自定义组件,否则不渲染任何东西。

    {previousDate && !moment(currentDate).isSame(previousDate, "day") ? ( // custom component) : null}

它应该是这样工作的,但主要问题是,我使用inverted FlatList 来让消息从屏幕底部到顶部。但是现在,由于它是一个倒置的平面列表,项目从底部到顶部呈现,它给了我这样的结果:

注意:一开始,消息也被颠倒了,但我通过发送它们也从数据库中颠倒了这个问题。

所以,我不知道如何才能实现我的目标,并像第一张图片那样做。

谢谢!

【问题讨论】:

  • 你解决了这个问题吗?...尝试相同但反应
  • 还没有,它还在我的 TODO 列表中

标签: react-native chat whatsapp


【解决方案1】:

我使用辅助函数 (generateItems) 来解决您所描述的问题。这是我用来按天对消息进行分组的代码,然后在 renderItem 属性中呈现 <Message /><Day />。如您所述,这是使用inverted FlatList

import moment from 'moment';

function groupedDays(messages) {
  return messages.reduce((acc, el, i) => {
    const messageDay = moment(el.created_at).format('YYYY-MM-DD');
    if (acc[messageDay]) {
      return { ...acc, [messageDay]: acc[messageDay].concat([el]) };
    }
    return { ...acc, [messageDay]: [el] };
  }, {});
}

function generateItems(messages) {
  const days = groupedDays(messages);
  const sortedDays = Object.keys(days).sort(
    (x, y) => moment(y, 'YYYY-MM-DD').unix() - moment(x, 'YYYY-MM-DD').unix()
  );
  const items = sortedDays.reduce((acc, date) => {
    const sortedMessages = days[date].sort(
      (x, y) => new Date(y.created_at) - new Date(x.created_at)
    );
    return acc.concat([...sortedMessages, { type: 'day', date, id: date }]);
  }, []);
  return items;
}

export default generateItems;

这里是我的列表以及renderItem 函数供参考:

<MessageList
  data={generatedItems}
  extraData={generatedItems}
  inverted
  keyExtractor={item => item.id.toString()}
  renderItem={renderItem}
/>

function renderItem({ item }) {
  if (item.type && item.type === 'day') {
    return <Day {...item} />;
  }
  return <Message {...item} />;
}

【讨论】:

  • 首先感谢 4 的回复。非常有趣的例子我会尝试一下......我希望你在写这篇文章时能理解我的问题。因为对我来说,分隔 renderItwem 元素是日期还是消息,我在项目内部做类似的事情,作为标题,但由于 flatlist 是倒置的,我将日期放在组件的末尾而不是标题。我希望你的例子能帮助我...谢谢
  • >,这里的acc、el和i是什么。
  • @SulakshithaRathnayake 这些是reduce 方法的标准变量:累加器、元素和索引。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • 好的。知道了。谢谢马特。
【解决方案2】:

这就是我的反应,

  1. 创建一个new Set() 以唯一地存储日期
const dates = new Set();
  1. 循环遍历 chats 数组时,检查渲染日期之前的唯一 Set 中是否已经存在日期
chats.map((chat) => {
  
  // For easier uniqueness check,
  // Formated date string example '16082021'
  const dateNum = format(chat.timestamp, 'ddMMyyyy');

  return (
    <React.Fragment key={chat.chat_key}>

      // Do not render date if it already exists in set
      {dates.has(dateNum) ? null : renderDate(chat, dateNum)}

      <ChatroomChatBubble chat={chat} />
    </React.Fragment>
  );
});
  1. 最后,当日期渲染完毕后,将日期编号添加到数组中,使其不再渲染
const renderDate = (chat, dateNum) => {
  const timestampDate = format(chat.timestamp, 'EEEE, dd/MM/yyyy');

  // Add to Set so it does not render again
  dates.add(dateNum);

  return <Text>{timestampDate}</Text>;
};

【讨论】:

  • 这种方法是使用FlatList吗?还是只是.map()?如果不在 RN 中使用FlatList,我认为对性能不利。
猜你喜欢
  • 2023-02-14
  • 2013-06-02
  • 2015-08-18
  • 2021-04-06
  • 1970-01-01
  • 2022-11-07
  • 2018-12-03
  • 2019-09-10
  • 1970-01-01
相关资源
最近更新 更多