Web Components, atributos e métodos de reação (parte 2)
Tags: javascript, webdev, webcomponents, frontend
Published: 2021-03-02 01:06:09

Essa é a segunda parte da série de tutoriais sobre Web Components, não deixe de ver a [primeira parte](https://dev.to/gabrieljm/web-components-o-que-sao-e-como-criar-parte-1-ale) que mostro o que são e como criar Web Components. Nesse tutorial vamos ver como criar uma interface de programação(API) para nossos componentes e veremos como funcionam suas funções reação.
## Definindo a API do elemento

Como comentado antes, assim que o elemento estende de `HTMLElement` é garantido que ele herde a API DOM que existe em comum entre os elementos HTML, isso também significa que todo atributo/propriedade e métodos da classe farão parte dessa API DOM também.

Vamos, criar um `<text-hider>`, um elemento simples que terá um **X** para esconder ou mostrar um texto.

```jsx
class TextHider extends HTMLElement {
  get show() {
    return this.hasAttribute('show')
  }

  set show(value) {
    if(typeof value !== 'boolean') {
      throw new TypeError(
        'text-hider "show" attribute only accepts boolean values'
      )
    }

    if(value) {
      this.setAttribute('show', '')
      this.querySelector('[text]').innerText = 'Showing the text'
    } else {
      this.removeAttribute('show')
      this.querySelector('[text]').innerText = ''
    }
  }
  
  constructor() {
    super()

    this.innerHTML = `
      <span>X</span>
      <span text>${this.show ? 'Showing the text' : ''}</span>
    `

    this.querySelector('span').addEventListener('click', () => {
      this.show = !this.show
    })
  }
}

customElements.define('text-hider', TextHider)
```

Repare no código acima que as funções getter e setter `show` representam a propriedade `show` que pode ser adicionada a tag `text-hider`. Ela pode ser acessa de forma programática da seguinte maneira:
```jsx
const textHider = new TextHider()
textHider.show = true
console.log(textHider.show) // true
```

Repare também em dois detalhes, um deles é que foi utilizado o método `querySelector` atrelado ao `this`, isso acontece porque o `this` dentro da classe se refere diretamente ao elemento que estamos criando e que como dito antes ele possui toda a API DOM comum aos elementos HTML. O outro detalhe se encontra dentro do `if` no método setter do atributo `show`, em que alteramos o innerText do segundo elemento span dependendo se o valor recebido era verdadeiro ou falso, fazendo isso somos capazes de ter uma certa reatividade no elemento, fazendo com que ele reaja à toda vez em que o valor de `show` é alterado.
## Métodos de reação

Elementos customizados podem definir métodos de ciclo de vida especiais, que são executados em momentos específicos da sua existência. Eles são conhecidos como **métodos de reação**.

- **constructor**: chamado quando um novo elemento é instanciado ou aprimorado. Útil para definir estados iniciais, adicionar eventos e afins.
- **connectedCallback**: chamado quando o elemento é inserido dentro do DOM, sendo o light DOM ou o shadow DOM de outro elemento(depois vamos falar sobre light DOM e shadow DOM). Útil para rodar código preparatório ou que receba alguma condição externa ao entrar no DOM.
- **disconnectedCallback**: chamado quando o elemento é removido do DOM. Útil para rodar códigos de limpeza, como remoção de ouvintes de evento.
- **attributeChangedCallback**: chamando quando algum atributo observado é adicionado, alterado ou removido. Esses atributos são listados no array retornado no método getter estático `observedAttributes`, logo mais falaremos sobre ele. Esse é o único método que recebe parâmetros, sendo o primeiro o nome do atributo alterado, o segundo sendo o valor antigo do atributo e o último sendo o novo valor.
- **adoptedCallback**: chamado quando o elemento é movido de **documento**. (por exemplo, quando alguém invoca `document.adoptNode(el)`).

Esses métodos de reação, são síncronos e serão imediatamente invocados quando as condições de invocação forem satisfeitas.
Não é necessário definir todos esses métodos, utilize deles quando fizer sentido.

```jsx
class MeuComponente extends HTMLElement {
  static get observedAttributes() {
    return []
  }
  
  constructor() {
    super()
  }

  connectedCallback() {...}

  disconnectedCallback() {...}

  attributeChangedCallback(name, oldValue, newValue) {...}

  adoptedCallback() {...}
}
```

## Propriedades e atributos

Como vimos antes podemos definir métodos para ter acesso aos atributos de um elemento de forma programática bem simples, além de ser um certo padrão de acesso aos atributos nos elementos HTML. Vamos usar uma div de exemplo alterando os atributos `hidden` e `id`:

```jsx
div.id = 'my-id'
div.hidden = true
```

A atribuição pelo JS, é refletida no HTML:

```html
<div id="my-id" hidden>
```

Isso pode ser chamado de "refletindo propriedades à atributos", lembrar desses detalhes é muito importante para ter a noção de que nosso elemento definido no JS está em sincronia com sua representação no DOM e isso pode até mesmo afetar estilizações que, por exemplo, usem seletores de atributos (`[attribute]`).

### Observando mudanças à atributos

Adicionar atributos a um elemento é uma forma comum de definir o estado inicial daquele elemento. Como vimos podemos adicionar um método `attributeChangedCallback` para que nosso elemento reaja a mudança nos atributos, porém ele somente será invocado caso o nome do atributo esteja no array retornado por `observedAttributes` e este método deve ser obrigatoriamente `static get`.

```jsx
static get observedAttributes() {
  return ['attribute']
}
```

Isso é implementado dessa maneira por questão de performance, para que seu código não seja invocado de formas desnecessárias, como por exemplo, quando mudar o atributo `class` e ser invocado `attributeChangedCallback`.

---
## Referências
https://developers.google.com/web/fundamentals/web-components/customelements

## Conclusão

Essas são as formas como podemos manipular as reações dos elementos e seus atributos. O conteúdo dessa parte é de certa forma simples, porém eu aconselho muito a treinar bastante e ver como os elementos reagem para entender cada detalhe melhor. Espero que gostem e qualquer dúvida deixe um comentário e até logo!!!