在 {#each} 循环内执行函数使用相反顺序的值,sveltekit

问题描述 投票:0回答:1

我在聊天应用程序中循环一些消息,并添加日期分隔元素来显示其下消息的发送日期,但是当我在同一天发送多条消息时,分隔符无法按预期工作,并且在应位于同一日期组中的消息之间移动。我有一个函数,可以获取日期并对其进行格式化,然后返回一个具有格式化日期(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}

我尝试通过索引引用各个消息,但它仍然被颠倒了

javascript arrays
1个回答
0
投票

您面临的问题可能是由于 Svelte 中渲染过程的异步性质造成的。当您在 {#each} 循环内执行函数时,循环的每次迭代都会调用该函数,但返回结果的顺序可能与循环迭代的顺序不同。

要解决这个问题,您可以尝试以下方法:

  1. 预计算日期组:不要在 {#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}
  1. 使用记忆函数:另一种方法是使用 formatDate() 函数的记忆版本,它将缓存结果并为相同的输入返回相同的值。这可以帮助确保函数以与循环迭代相同的顺序返回相同的结果。
// 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;
        }
    };
}
© www.soinside.com 2019 - 2024. All rights reserved.