我在聊天应用程序中循环一些消息,并添加日期分隔元素来显示其下消息的发送日期,但是当我在同一天发送多条消息时,分隔符无法按预期工作,并且在应位于同一日期组中的消息之间移动。我有一个函数,可以获取日期并对其进行格式化,然后返回一个具有格式化日期(dd-mm-yyyy)和时间(小时-分钟 AM/PM)的对象。当我将 {#each} 循环内的函数添加到 console.log formatDate() 的结果时,它以相反的顺序显示在控制台中。例如,如果我在上午 8:35 发送一条消息,并在上午 8:37 发送第二条消息,则控制台中首先显示 8:37,然后显示 8:35。包含消息的数组按时间顺序排序 顺序,当我显示数组中元素的索引或将它们记录到控制台时,它们是按顺序排列的。我认为可能存在一些异步操作,导致循环引用值的顺序不同。
let datesGroup: string[] = [];
// Function to get the moment of the day and of the year when the message was sent
function formatDate(dateStr: string, addToArray: boolean = false): MessageDate {
if (!dateStr) return { ofYear: '', ofDay: '' };
// Set pointers in time
const date = new Date(dateStr);
const todayDate = new Date();
const { hour, minute, meridian, day, month, year } = getDateValues(date);
const yearDate = `${day}-${month}-${year}`;
const time = `${hour}:${minute} ${meridian}`;
const today = `${getDateValues(todayDate).day}-${getDateValues(todayDate).month}-${getDateValues(todayDate).year}`;
let isNewDate = false;
// Check if a message is part of a different date group
if (datesGroup.indexOf(yearDate) === -1) isNewDate = true;
// Add the date divider to the array
if (addToArray && isNewDate) datesGroup = [...datesGroup, yearDate];
return {
isNewDate: isNewDate,
ofDay: time,
ofYear: today === yearDate ? 'Today' : yearDate
};
}
function getDateValues(date: Date) {
return {
minute: date.getMinutes() > 9 ? date.getMinutes() : '0' + date.getMinutes(),
hour: date.getHours() > 12 ? date.getHours() - 12 : date.getHours(),
day: date.getDate() > 9 ? date.getDate() : '0' + date.getDate(),
month: date.getMonth() > 9 ? date.getMonth() : '0' + date.getMonth(),
year: date.getFullYear(),
meridian: date.getHours() > 12 ? 'PM' : 'AM'
};
}
{#each messages as message (message.id)}
{#if formatDate(String(message.sentat), false).isNewDate}
<div class="date-display">
{formatDate(String(message.sentat), true).ofYear}
</div>
{/if}
<div>
{#if message.from != $page.data.USER.id}
<img
class="msg-profile-picture"
alt="pfp"
src={getProfilePicture(message.from, currentRoomMembers)}
/>
{/if}
<div class="msg-content" class:sent-by-me={$page.data.USER.id === message.from}>
{#if message.from !== $page.data.USER.id}
<span
><a href="/profiles/{message.from}">{GetUsername(message.from, currentRoomMembers)}</a
></span
>
{/if}
<div class:shortened={message.text.length > 1400 && !message.shortened}>
{message.text.length < 1400
? message.text
: message.text.split('').slice(0, 1400).join('')}
{#if message.text.length > 1400}
<button type="button" class="show-more">Show more</button>
{/if}
</div>
<span>{formatDate(String(message.sentat), false).ofDay}</span>
</div>
</div>
{/each}
我尝试通过索引引用各个消息,但它仍然被颠倒了
您面临的问题可能是由于 Svelte 中渲染过程的异步性质造成的。当您在 {#each} 循环内执行函数时,循环的每次迭代都会调用该函数,但返回结果的顺序可能与循环迭代的顺序不同。
要解决这个问题,您可以尝试以下方法:
// Precompute the date groups
let datesGroup: string[] = [];
messages.forEach((message) => {
const { ofYear } = formatDate(String(message.sentat), true);
if (!datesGroup.includes(ofYear)) {
datesGroup = [...datesGroup, ofYear];
}
});
{#each messages as message (message.id)}
{#if datesGroup.indexOf(formatDate(String(message.sentat), false).ofYear) !== -1}
<div class="date-display">
{formatDate(String(message.sentat), false).ofYear}
</div>
{/if}
{/* Rest of the loop content */}
{/each}
// Memoized formatDate function
const memoizedFormatDate = memoize((dateStr) => formatDate(dateStr, false));
{#each messages as message (message.id)}
{#if memoizedFormatDate(String(message.sentat)).isNewDate}
<div class="date-display">
{memoizedFormatDate(String(message.sentat)).ofYear}
</div>
{/if}
{/* Rest of the loop content */}
{/each}
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
} else {
const result = fn.apply(this, args);
cache.set(key, result);
return result;
}
};
}