我怎样才能像 Whatsapp 聊天屏幕那样按组显示日期?
How can I show days by group like Whatsapp chat screen?
我怎样才能像 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
prop 创建了一个功能组件,因此 item
和 index
来自 FlatList 的实际数据。
我在这里要做的是,基本上是抓取当前渲染项目的 created_at
并将其与前一个项目的 created_at
进行比较,为此,我使用的是原始渲染项目存储在状态中的数据。但不幸的是,当 FlatList 渲染第一个索引号为 0
的项目时,状态中的原始数据中没有要比较的上一个元素,这就是为什么我检查是否大于 0 去从上一个索引项目中获取日期.在 Else 的情况下,这意味着在呈现第一个项目时,不要查找上一个项目,而只获取 created_at
。
下面我检查 currentDate
和 previousDate
是否不一样,渲染一个自定义组件,否则不渲染任何东西。
{previousDate && !moment(currentDate).isSame(previousDate, "day") ? ( // custom component) : null}
它应该像那样工作,但主要问题是,我使用 inverted
FlatList 来使消息从屏幕底部到顶部。但是现在,因为它是一个倒置的平面列表,所以项目从底部到顶部呈现,它给了我这样的结果:
注意:一开始消息也被逆转了,但我通过发送它们也从数据库中逆转来解决了这个问题。
所以,我不知道如何才能实现我的目标,并像第一张图片那样做到。
谢谢!
我使用辅助函数 (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} />;
}
这就是我在 React 中的做法,
- 创建一个
new Set()
来唯一地存储日期
const dates = new Set();
- 当遍历聊天数组时,在渲染日期
之前检查唯一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>
);
});
- 最后,当 date 渲染完成后,将 date num 添加到数组中,这样它就不会再次渲染了
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>;
};
我怎样才能像 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
prop 创建了一个功能组件,因此 item
和 index
来自 FlatList 的实际数据。
我在这里要做的是,基本上是抓取当前渲染项目的 created_at
并将其与前一个项目的 created_at
进行比较,为此,我使用的是原始渲染项目存储在状态中的数据。但不幸的是,当 FlatList 渲染第一个索引号为 0
的项目时,状态中的原始数据中没有要比较的上一个元素,这就是为什么我检查是否大于 0 去从上一个索引项目中获取日期.在 Else 的情况下,这意味着在呈现第一个项目时,不要查找上一个项目,而只获取 created_at
。
下面我检查 currentDate
和 previousDate
是否不一样,渲染一个自定义组件,否则不渲染任何东西。
{previousDate && !moment(currentDate).isSame(previousDate, "day") ? ( // custom component) : null}
它应该像那样工作,但主要问题是,我使用 inverted
FlatList 来使消息从屏幕底部到顶部。但是现在,因为它是一个倒置的平面列表,所以项目从底部到顶部呈现,它给了我这样的结果:
注意:一开始消息也被逆转了,但我通过发送它们也从数据库中逆转来解决了这个问题。
所以,我不知道如何才能实现我的目标,并像第一张图片那样做到。
谢谢!
我使用辅助函数 (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} />;
}
这就是我在 React 中的做法,
- 创建一个
new Set()
来唯一地存储日期
const dates = new Set();
- 当遍历聊天数组时,在渲染日期 之前检查唯一
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>
);
});
- 最后,当 date 渲染完成后,将 date num 添加到数组中,这样它就不会再次渲染了
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>;
};