Une autre façon de suivre les structures de données imbriquées

L’état @tracked d’un tableau ou d’un objet dans un composant Glimmer ne déclenche pas de mises à jour lorsque vous modifiez un membre du tableau/objet.

Ce sujet aborde quelques options pour que votre vue se mette à jour correctement. Je voulais ajouter une autre méthode, car les options abordées là-bas n’ont pas fonctionné pour moi.

Voici comment j’ai résolu ce problème : au lieu d’assigner un élément à la fois, assignez le tableau entier en une seule fois. Cette même stratégie fonctionnerait avec un objet ou toute autre structure de données.

Dans un composant 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
  })

  // C'EST LA LIGNE À REMARQUER
  // Parce que nous utilisons = directement sur le tableau, cela déclenchera une mise à jour.
  // Alors que pousser dans le tableau ou définir des éléments par index ne déclencherait pas de mise à jour.
  this.categories = await Promise.all(waiting)
}

async loadData(url) {
  try {
    const data = await ajax(url);
    //console.log(`Got data for ${url}:`, data)
    return data
  } catch (error) {
    console.error(`Failed to get data for ${url}:`, error);
  }
}

Vous pourriez faire cela avec une mise à jour partielle également. L’important est que JavaScript détecte qu’une valeur différente est assignée. Ainsi, bien que .concat fonctionne, car il génère un nouveau tableau distinct de l’ancien :

this.categories = this.categories.concat(newValue)

Utiliser .push ne fonctionnera probablement pas, car le tableau résultant a le même ID d’objet que l’original, donc JavaScript pense que rien n’a changé.

this.categories.push(newValue)
this.categories = this.categories

Si vous travaillez avec un objet, un moyen simple d’en générer un nouveau est d’utiliser Object.assign() - qui crée une nouvelle structure de données étendue, comme .concat ci-dessus.

C’est le cas depuis longtemps pour d’autres frameworks comme Vue.js également - vous pouvez trouver des sujets à ce sujet datant de 10 ans. Cela a à voir avec la façon dont c’est implémenté en JavaScript, probablement soit des getters/setters sur l’objet parent, soit l’API observe. Mais si c’est implémenté avec l’API observe, alors il y a un peu moins d’excuse pour ce comportement, car il serait facile d’activer le drapeau subtree… Peut-être qu’ils choisissent de ne pas le faire pour des raisons de performance lors de la surveillance d’une structure profondément imbriquée ? Mais je m’égare, ce sont des détails d’implémentation des bibliothèques sous-jacentes. La solution de contournement ci-dessus devrait vous aider à naviguer dans ce problème.

2 « J'aime »

Je me demande si TrackedArray pourrait aider ici ?
Je n’ai pas approfondi. Je me suis juste rappelé que le code de Discourse l’utilise ici et là.

import { TrackedArray, TrackedObject } from \"@ember-compat/tracked-built-ins\";

2 « J'aime »

Imaginez si c’était documenté. :rire:

Mais oui, cela semble conçu précisément pour ce scénario.

2 « J'aime »