#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 у меня есть другая БД с гораздо меньшим количеством шаблонов, поэтому я не смог воспроизвести это).
Итак, любой, кто сталкивается с подобной проблемой: проверьте, действительно ли ваши ключи уникальны на странице, потому что то, что происходит после загрузки новых шаблонов:
- React хочет удалить старый элемент
- Он выполняет поиск соответствующего элемента DOM для каждого ключа и находит элемент с
key="XXX"
помощью и удаляет его из DOM - Их дублированные элементы остаются в DOM, поскольку был обработан соответствующий ключ, и React больше не заботится об этом конкретном ключе
- Мы загружаем новые шаблоны, и они добавляются в родительский контейнер
- Вуаля! У нас есть старые элементы перед новой загрузкой
То, что я сделал здесь — мой ключ сейчас:
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)