Detecção de Alterações do Componente Glimmer Não Funciona - Mutando Propriedades em Array @tracked

Olá a todos!

Estou tendo problemas com a detecção de alterações em um componente Glimmer e gostaria de alguma orientação. Quando eu muto propriedades em objetos dentro de um array \@tracked\\, o template não é renderizado novamente.

\\Minha Configuração:\\

```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

    }

    // ... mais itens

  ];



  @action

  toggleExpanded(item) {

    item.expanded = !item.expanded; // Esta mutação não aciona a nova renderização

    console.log('Toggled:', item.label, item.expanded); // Registra corretamente

  }



  <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>

}

```

\\O Problema:\\

  1. O manipulador de clique executa corretamente
  2. A propriedade \item.expanded\\ é atualizada (verificada no console)
  3. Mas o template não é renderizado novamente - nenhuma alteração de classe, nenhuma alteração de texto

\\O que eu Tentei:\\

  1. \\Reatribuição de array\\ - \this.items = \\[...this.items\\]\\ após a mutação (não funciona)
  2. \\Immer para atualizações imutáveis\\ - Queria tentar isso, mas não consigo fazer as importações npm funcionarem em temas do Discourse
  3. \\Diferentes estratégias de chave\\ - \key="id"\\ em vez de \key="@index"\\

\\Perguntas:\\

  • Mutar propriedades em objetos dentro de arrays \@tracked\\ deveria funcionar no Glimmer?
  • Preciso rastrear objetos individuais de alguma forma?
  • Existe um padrão recomendado para este caso de uso?
  • Isso pode estar relacionado à execução em um ambiente de tema do Discourse?
  • Existem limitações com importações npm em temas do Discourse que afetam bibliotecas de reatividade?

\\Ambiente:\\

  • Componente de tema do Discourse
  • Componentes Glimmer com arquivos \.gjs\\
  • Versão mais recente do Discourse - atualizada hoje

Qualquer insight seria muito apreciado! Há algo fundamental que estou perdendo sobre o sistema de reatividade do Glimmer?

Obrigado!

2 curtidas

Hmm, isso é estranho. Fazer o truque de atribuição de array geralmente funciona.

Uma alternativa que encontrei é fazer com que os objetos no array sejam de uma classe com suas próprias propriedades 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 do seu código
}

Pode ser mais verboso do que criar um monte de objetos simples, mas descobri que facilita a extensão e a compreensão, especialmente se você precisar fazer algo como passar os dados para componentes aninhados.

4 curtidas

Olá Alteras,

Obrigado pela sua sugestão !!! Funcionou como você sugeriu e realmente tornou nosso dia um pouco mais fácil ::)** :grinning_face:

2 curtidas

Acredito que, ao rastrear um array como descrito no OP, você está rastreando a referência do array e não as alterações nos objetos individuais dentro dele.

Outra maneira de lidar com isso é usar trackedObject. Usamos isso em alguns lugares no Discourse.

3 curtidas

Obrigado pela sua contribuição. Tenho mais algumas perguntas:

Usar uma biblioteca como o Immer funcionaria?
Se sim, como devo incluir o Immer no Discourse?
Vi menções de ‘copiá-lo para os assets a partir do node_modules’, mas eu preferiria se houvesse uma maneira de fazer isso via npm, ou mesmo um link CDN na tag do cabeçalho.

Você pode usar loadScript() para carregar JS, mesmo que a URL fornecida seja uma URL externa. Usar serviços externos como jsdelivr deve funcionar com isso.

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

...

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

Embora eu não tenha testado extensivamente, um import dinâmico também deve funcionar.

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

Devo notar que é provavelmente mais seguro salvar os arquivos NPM diretamente em seu código em vez de depender de uma CDN externa, dependendo de quão crítico o JS é. Da mesma forma, você pode precisar fazer um pouco de empacotamento e pré-processamento antes de poder usá-lo no navegador.


Quanto a saber se o Immer funcionará com o discourse, eu não sei.

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