@tracked состояние массива или объекта внутри компонента Glimmer не вызывает обновлений при изменении элемента массива/объекта.
В этой теме обсуждаются некоторые варианты для корректного обновления представления. Я хотел добавить ещё один способ, так как описанные там варианты мне не подошли.
Вот как я решил эту проблему: вместо присваивания одного элемента за раз, присваивайте весь массив целиком. Этот же подход работает с объектами и любой другой структурой данных.
В компоненте Glimmer:
@tracked categories = [];
constructor() {
super(...arguments);
this.loadAllData();
}
async loadAllData() {
const categories = []
const waiting = this.siteSettings.county_fence_print_categories.split('|').map(async (cid, index) => {
const data = await this.loadData(`/c/${cid}/l/latest.json?period=monthly`)
return data.topic_list.topics
})
// ОБРАТИТЕ ВНИМАНИЕ НА ЭТУ СТРОКУ
// Поскольку мы используем присваивание (=) напрямую к массиву, это вызовет обновление.
// В то время как добавление элементов через push или установка по индексу не вызовут обновления.
this.categories = await Promise.all(waiting)
}
async loadData(url) {
try {
const data = await ajax(url);
//console.log(`Получены данные для ${url}:`, data)
return data
} catch (error) {
console.error(`Не удалось получить данные для ${url}:`, error);
}
}
Вы также можете сделать это с частичным обновлением. Главное, чтобы JavaScript обнаружил, что присваивается новое значение. Таким образом, .concat сработает, так как он создаёт новый массив, отличный от старого:
this.categories = this.categories.concat(newValue)
Использование .push, скорее всего, не сработает, так как результирующий массив будет иметь тот же идентификатор объекта, что и исходный, поэтому JavaScript подумает, что ничего не изменилось.
this.categories.push(newValue)
this.categories = this.categories
Если вы работаете с объектом, простой способ создать новый — использовать Object.assign(), который создаёт новую, расширенную структуру данных, аналогично .concat выше.
Это уже давно известно для других фреймворков, таких как Vue.js — можно найти темы об этом, уходящие на 10 лет назад. Это связано с тем, как это реализовано в JavaScript, вероятно, через геттеры/сеттеры родительского объекта или API observe. Но если это реализовано через API observe, то у такого поведения меньше оправданий, поскольку можно было бы легко переключить флаг поддерева… Возможно, они не делают этого из соображений производительности при отслеживании глубоко вложенных структур? Но я отвлекся, это детали реализации базовых библиотек. Приведённый выше обходной путь должен помочь вам справиться с этой проблемой.