React сохраняет дочерние элементы от предыдущего рендеринга

#reactjs #react-redux

#reactjs #react-redux

Вопрос:

У меня проблема с рендерингом страницы на основе массива шаблонов, хранящихся в redux. Я не могу предоставить фактический код, поскольку он слишком сложен для воспроизведения в изолированной среде, но я попробую использовать упрощенную структуру.

У меня есть сетка в стиле pinterest, основанная на flex-grow вычислениях и js для каждого элемента на основе его размеров, поэтому мне нужно, чтобы все это было в одном контейнере.

У меня есть 3 группы элементов в одном div:

blank_item templates.map tailItems.map

 <div className="grid">
    {shouldRenderBlankItem amp;amp; <BlankItem />}
    {templates.map((template) => (
        <Template
            key={template.uuid}
            template={template}
        />
    ))}
    {shouldRenderTail amp;amp;
        tailItems.map(item, i) => (
            <TailItem
                key={i}
                item={item}
            />
        ))}
</div>
  

Проблема в том, что иногда после рендеринга у меня остаются ДОПОЛНИТЕЛЬНЫЕ дочерние элементы от предыдущего рендеринга, и React помещает их перед другими элементами внутри div.grid , поэтому результат, который я получу, будет выглядеть так:

3-4 of EXTRA <Template/> blank_item templates.map tailItems.map

В качестве ключа для шаблона, который я использую template.uuid , который поступает из серверной части, и я знаю, что они уникальны, и react также не показывает никаких предупреждений для дублированных ключей — поэтому я знаю, что я не получаю никаких дублированных элементов из API, что, как я думал, может быть проблемой.

Redux в порядке — я вижу templates в нем правильное количество, например, 50, grid а инструменты react dev tools показывают те же 50 шаблонов, которые используются в качестве опоры, когда я проверяю родительский компонент, но фактический DOM содержит, например, 53 элемента в тот же момент.

Как я могу отладить подобную проблему? Я полагаю, что что-то не так во время согласования, но я не знаю, с чего именно начать отладку.

У меня есть React / ReactDOM 16.13.1

Комментарии:

1. Распространенной ошибкой, которую я допустил раньше, что привело к такой проблеме, было изменение какого-то состояния где-то, но, говоря, что это тот случай, просто указывая.

2. @ShmiliBreuer спасибо, приятель, единственная подозрительная часть в reducer, которую я вижу, const items = shouldAppendItems ? [...state.items, ...newItemsFromApi] : newItemsFromApi; return {...state, items} но мне кажется, что это правильный путь (случай, когда я объединяю 2 массива вместе, используется для загрузки большего количества элементов на одну и ту же страницу, и это работает нормально, случай, когда у меня проблема, когда я получаю newItemsFromApi и ясм. В redux есть только те элементы в хранилище)

Ответ №1:

Хорошо, итак, в моем случае проблема заключалась в сломанном интерфейсном api, который мог возвращать один и тот же шаблон дважды — поэтому идентификаторы uuid, которые я использую для ключей, были уникальными для каждого шаблона, но на самом деле они не уникальны с точки зрения элементов страницы и DOM.

Я столкнулся с этим только в рабочей сборке, поэтому у меня не было duplicated key предупреждения (в dev build у меня есть другая БД с гораздо меньшим количеством шаблонов, поэтому я не смог воспроизвести это).

Итак, любой, кто сталкивается с подобной проблемой: проверьте, действительно ли ваши ключи уникальны на странице, потому что то, что происходит после загрузки новых шаблонов:

  1. React хочет удалить старый элемент
  2. Он выполняет поиск соответствующего элемента DOM для каждого ключа и находит элемент с key="XXX" помощью и удаляет его из DOM
  3. Их дублированные элементы остаются в DOM, поскольку был обработан соответствующий ключ, и React больше не заботится об этом конкретном ключе
  4. Мы загружаем новые шаблоны, и они добавляются в родительский контейнер
  5. Вуаля! У нас есть старые элементы перед новой загрузкой

То, что я сделал здесь — мой ключ сейчас:

 templates.map((template, i) => <Template key={template.uuid   i} />
  

Так что в этом случае я в безопасности, даже если серверная часть возвращает дублированные элементы.
Конечно, мы также исправим бэкэнд. Спасибо за чтение и помощь!

Просто быстрый пример, как искать элементы с одинаковыми id data :

 var duplicates = new Set();
data.map(t => t.id).forEach((id, i, arr) => {if(arr.filter(item => item == id).length > 1){
duplicates.add(id);
}});
console.log("DUPLICATED IDs:", duplicates)