Detección de cambios de componentes Glimmer no funciona: mutación de propiedades en arreglo @tracked

Hola a todos!

Tengo problemas con la detección de cambios en un componente Glimmer y agradecería algo de orientación. Cuando muto propiedades en objetos dentro de un array \@tracked\\, la plantilla no se vuelve a renderizar.

\\Mi Configuración:\\

```javascript

import Component from '@glimmer/component';

import { tracked } from '@glimmer/tracking';

import { fn } from '@ember/helper';

import { on } from '@ember/modifier';

import { action } from '@ember/object';



export default class CustomSidebarComponent extends Component {

  @tracked items = [

    {

      id: 'home',

      label: 'Home',

      expanded: false

    },

    {

      id: 'my-posts', 

      label: 'My Posts',

      expanded: false

    }

    // ... más elementos

  ];



  @action

  toggleExpanded(item) {

    item.expanded = !item.expanded; // Esta mutación no activa la re-renderización

    console.log('Toggled:', item.label, item.expanded); // Se registra correctamente

  }



  <template>
    <div id="custom-sidebar">
      <ul>
        {{#each this.items key="@index" as |item|}}
          <li class="menu-item {{if item.expanded 'expanded'}}" {{on "click" (fn this.toggleExpanded item)}}>
            {{item.label}} {{if item.expanded "(expanded)" ""}}
          </li>
        {{/each}}
      </ul>
    </div>
  </template>

}

```

\\El Problema:\\

  1. El manejador de clic se ejecuta correctamente
  2. La propiedad \item.expanded\\ se actualiza (verificado en la consola)
  3. Pero la plantilla no se vuelve a renderizar: no hay cambios de clase, no hay cambios de texto.

\\Lo que he intentado:\\

  1. \\Reasignación de array\\ - \this.items = \\[...this.items\\]\\ después de la mutación (no funciona)
  2. \\Immer para actualizaciones inmutables\\ - Quería probar esto pero no puedo hacer que las importaciones de npm funcionen en los temas de Discourse
  3. \\Estrategias de clave diferentes\\ - \key="id"\\ en lugar de \key="@index"\\

\\Preguntas:\\

\- ¿Se supone que la mutación de propiedades en objetos dentro de arrays \@tracked\\ funcione en Glimmer?
\- ¿Necesito hacer que los objetos individuales se rastreen de alguna manera?
\- ¿Existe un patrón recomendado para este caso de uso?
\- ¿Podría estar relacionado con la ejecución en un entorno de tema de Discourse?
\- ¿Existen limitaciones con las importaciones de npm en los temas de Discourse que afecten a las bibliotecas de reactividad?

\\Entorno:\\

\- Componente de tema de Discourse
\- Componentes Glimmer con archivos \.gjs\\
\- Última versión de Discourse - actualizada hoy

¡Cualquier información sería muy apreciada! ¿Hay algo fundamental que me esté perdiendo sobre el sistema de reactividad de Glimmer?

¡Gracias!

2 Me gusta

Hmm, eso es raro. Hacer el truco de asignación de arrays normalmente funciona.

Una alternativa que he encontrado es hacer que los objetos en el array sean de una clase con sus propias propiedades rastreadas. Algo como:

class CustomSidebarItem {
  @tracked expanded = false;
  constructor(id, label) {
    this.id = id;
    this.label = label;
  }
}

export default class CustomSidebarComponent extends Component {

  @tracked items = [
    new CustomSidebarItem('home', 'Home'),
    new CustomSidebarItem('my-posts', 'My Posts'),
    ...
  ];
  // resto de tu código
}

Puede ser más verboso que crear un montón de objetos planos, pero he descubierto que hace que sea más fácil de extender y razonar, especialmente si necesitas hacer algo como pasar los datos a componentes anidados.

4 Me gusta

Hola Alteras,

¡¡¡Gracias por tu sugerencia!!! Funcionó como lo sugeriste y realmente nos facilitó un poco el día ::)** :grinning_face:

2 Me gusta

Creo que cuando rastreas un array como se describe en OP, estás rastreando la referencia del array y no los cambios en los objetos individuales dentro del array.

Otra forma de manejarlo es usando trackedObject, lo usamos en varios lugares en Discourse.

3 Me gusta

Gracias por tu aporte. Tengo otra pregunta de seguimiento:

¿Funcionaría usar una biblioteca como Immer?
Si es así, ¿cómo debería incluir Immer en Discourse?
He visto menciones de “copiarlo en los assets desde node_modules”, pero preferiría si hubiera una forma de hacerlo a través de npm, o incluso un enlace CDN en la etiqueta del encabezado.

Puedes usar loadScript() para cargar JS, incluso si la URL proporcionada es una URL externa. Usar servicios externos como jsdelivr debería funcionar con esto.

import loadScript from "discourse/lib/load-script";

...

loadScript(URL).then(() => {
   // tu código
})

Aunque no lo he probado exhaustivamente, una importación dinámica también debería funcionar.

async function load() {
  const {
    default: myDefault,
    foo,
    bar,
  } = await import(URL);
  // resto del código
}

Debo señalar que probablemente sea más seguro guardar los archivos NPM directamente en tu código en lugar de depender de una CDN externa, dependiendo de cuán crítico sea el JS. De manera similar, es posible que necesites hacer un poco de empaquetado y preprocesamiento antes de poder usarlo en el navegador.


En cuanto a si Immer funcionará con Discourse, no lo sé.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.