Vue3: como declarar estados reativos

sucodelarangela

Angela Caldas

Posted on March 6, 2024

Vue3: como declarar estados reativos

Read it in English

Em uma Single Page Application (SPA), a reatividade é a capacidade que o aplicativo tem de responder dinamicamente a alterações em seus dados, atualizando partes da página sem a necessidade de recarregá-la do zero.

Quando o Vue identifica mudanças em seus estados, ele usa o virtual DOM para re-renderizar a interface e garantir que ela sempre esteja atualizada, tornando o processo o mais rápido e eficiente possível.

Existem diferentes formas de declarar estados reativos em uma aplicação Vue, que podem variar dependendo da API que estiver em uso (Options ou Composition), além dos tipos de dados em si. E é isso que vamos abordar nesse artigo!

Sumário
Dados reativos com Options API:
Data properties
Computed Properties
Diferença entre métodos e propriedades computadas
Dados reativos com Composition API
ref()
reactive()
Limitações do reactive()
computed()
Em resumo...

Reação química


Dados reativos com Options API

Data properties

Com a Options API, declaramos estados reativos de um componente através das propriedades de dados (data properties), utilizando o método data para tal. Esse método retorna um objeto com todos os estados reativos e seus valores.

<script>
export default {
  data () {
    counter: 0
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

No exemplo acima, counter é um dado reativo e, caso seja alterado, a interface atualizará automaticamente para refletir as alterações.

Para vermos a reatividade em ação, vamos criar um método (ou função) que modifica o valor de counter cada vez que clicarmos um botão.

<script>
export default {
  data () {
    counter: 0
  },
  methods: {
    increaseCounter() {
      this.counter++
    }
  }
}
</script>
Enter fullscreen mode Exit fullscreen mode

Counter atualizando

Veja que, ao clicar no botão, o valor de counter é automaticamente atualizado na interface.

É importante que todos os dados reativos a serem utilizados no componente sejam declarados na opção data mesmo que ainda não tenham um valor definido, pois eles serão instanciados no componente assim que ele for criado (proporcionando o uso de this em métodos e ciclos de vida).

Caso precise iniciar um estado sem valor definido, você pode usar undefined, null ou agum outro valor que sirva de placeholder (como uma string vazia, por exemplo).


Computed Properties

As propriedades computadas (computed properties) são utilizadas quando precisamos fazer cálculos ou outras lógicas baseadas em data properties de forma passiva. Ou seja, quando um dado reativo é alterado, todas as propriedades computadas que dependem desse dado também são atualizadas. É como se elas fossem funções formatadoras de variáveis.

Para declarar propriedades computadas, usamos a sintaxe de função dentro da opção computed. Essa função deve sempre retornar um valor.

<script>
  export default {
    // Código oculto
    computed: {
      doubleCounter() {
        return this.counter * 2
      }
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Note que não utilizamos a sintaxe de arrow function em nossa propriedade computada, pois estas não possuem this em seu contexto, impossibilitando que possamos acessar nossas data properties.

A propriedade computada acima retorna sempre o dobro do valor de counter. Podemos ver em nossa aplicação que, sempre que counter for modificado, o valor de doubleCounter é automaticamente atualizado:

doubleCounter atualizando

O mesmo resultado poderia ser obtido diretamente no template do nosso componente inserindo a lógica dentro da interpolação ({{ }} também chamada de mustache, nosso bom "bigodinho").

<template>
  <p>The double of counter is: {{ doubleCounter }}</p>
  <!-- VIRARIA -->
  <p>The double of counter is: {{ counter * 2 }}</p>
<template>
Enter fullscreen mode Exit fullscreen mode

Dessa forma, nossa aplicação funcionaria da mesma maneira. Porém, em aplicações maiores essas lógicas podem ficar bem mais complexas e, com o uso das propriedades computadas, podemos deixar nosso template mais fácil de entender e nosso código mais organizado.


Diferença entre métodos e propriedades computadas

É fácil perceber que a mesma lógica de doubleCounter poderia ser alcançada através de um método em vez de uma propriedade computada, concorda?

<template>
  <p>{{ doubleCounter() }}</p>
<template>

<script>
// código oculto
  methods: {
    doubleCounter() {
      return this.counter * 2
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

Com o código acima, poderíamos facilmente obter o mesmo resultado. E por que não optamos por essa alternativa? Porque propriedades computadas são armazenadas em cache com base nos dados reativos dos quais depende.

E o que isso significa?

Significa que, enquanto counter não sofrer modificação, doubleCounter também não mudará e sempre retornará o último resultado computado independente de quantas vezes você re-renderizar o seu componente. Por outro lado, métodos sempre serão executados quando o componente for re-renderizado, mesmo que nosso dado reativo counter não mude.

Porque precisamos de caching? Imagine que temos uma enorme lista como propriedade computada, que faz loops em um array enorme e faz vários cálculos. E então temos outras propriedades computadas que dependem dessa lista. Sem caching, executaríamos o getter da lista muito mais vezes que o necessário (quando podemos executar apenas quando a lista sofrer alterações)! — Extraído da documentação do Vue.

Dessa forma, vamos usar métodos apenas quando não precisarmos de caching! 😉


Dados reativos com Composition API

No Vue 3, a reatividade é alcançada através do sistema conhecido como "Reatividade Proxy-based", que cria proxies em torno dos objetos de dados e permitem interceptar operações de leitura (get) e escrita (set) nas propriedades desses objetos. Este sistema é uma evolução do sistema de reatividade utilizado nas versões anteriores do Vue (que utilizavam Object. defineProperty), proporcionando um desempenho mais eficiente e com menos limitações.

Dessa forma, devido a essa atualização, com a Composition API temos formas diferentes de declarar estados reativos. Aqui saem as data properties e entram as funções ref() e reactive() na jogada!


ref()

Para declarar dados reativos usando a função ref(), devemos usar a sintaxe de declaração de variáveis com const. ref() é comumente utilizado para estados com tipos primitivos (como string, number, boolean, etc.). Vamos utilizar o mesmo exemplo de counter:

<script setup>
  import { ref } from "vue";

  const counter = ref(0);

  const increaseCounter = () => {
    counter.value++;
  };
</script>
Enter fullscreen mode Exit fullscreen mode

Quando estudamos as diferenças entre Options e Composition API, vimos que precisamos importar as funções nativas do Vue que vamos utilizar em nosso componente. Então, começamos importando ref e usando-o em nossa variável counter para englobar o valor inicial do nosso estado.

Para alterar o valor de counter, criamos uma arrow function que modifica a propriedade .value da nossa ref. Essa propriedade é criada automaticamente pelo sistema de reatividade do Vue 3, não sendo necessário declará-la ao criar nosso estado com ref(). E é essa propriedade que permite que o Vue detecte se um estado foi acessado ou alterado.

Counter atualizando

Note que, quando usamos nosso estado computado diretamente no template, não precisamos usar a propriedade .value.


reactive()

Reactive é a segunda forma que o Vue disponibiliza para declaração de dados reativos. Diferente de ref, que engloba o valor do dado em um objeto interno com a propriedade .value, a função reactive() torna dados do tipo objeto em um objeto reativo. Logo, reactive() tem a limitação de não aceitar strings, números e booleans.

Ainda mantendo o exemplo de counter, podemos refatorar nosso código para utilizar reactive():

<script setup>
  import { reactive} from "vue";

  const state = reactive({ counter: 0 });

  const increaseCounter = () => {
    state.counter++;
  };
</script>
Enter fullscreen mode Exit fullscreen mode

No código acima, nossa variável state agora recebe um objeto { counter: 0 } como dado reativo. Nesse caso, se quisermos alterar o valor de counter, podemos acessar diretamente a chave counter dentro do objeto state no nosso método increaseCounter.

State counter atualizando

Note que em nosso template também precisamos acessar diretamente state.counter para que seu valor seja renderizado corretamente na tela.


Limitações do reactive()

Como dito mais acima, a primeira limitação da função reactive() é não aceitar string, number e boolean. Sendo assim, essa função deve ser usada apenas para dados do tipo objeto (como os próprios objetos, arrays e coleções, como Map e Set).

Também não é possível reatribuir um novo valor ao mesmo objeto reativo, ou seja, não é possível substituir o objeto inteiro por um outro objeto, pois o sistema de reatividade do Vue trabalha diretamente com o acesso às propriedades.

// Não é possível reatribuir o valor de state
let state = reactive({ count: 0 })
state = reactive({ count: 1 })
Enter fullscreen mode Exit fullscreen mode

O uso de reactive() também não permite desestruturação, pois a referência de conexão da reatividade é perdida:

const state = reactive({ count: 0 })
// count é desconectado de state.count quando desestruturado
let { count } = state
Enter fullscreen mode Exit fullscreen mode

computed()

O uso de propriedades computadas mantém o mesmo propósito visto anteriormente: executar cálculos e lógicas de forma passiva de acordo com a mudança detectada em uma ref() ou reactive().

Porém, com a Composition API, temos uma sintaxe diferente para a criação dessas propriedades computadas: computed() agora é uma função que recebe como parâmetro outra função e retorna uma ref computada. Vamos ao exemplo:

<script setup>
import { ref, computed } from "vue";

// código ocultado

const doubleCounter = computed(() => {
  return counter.value * 2;
});
</script>
Enter fullscreen mode Exit fullscreen mode

No código acima, declaramos nossa propriedade computada como uma variável e usamos a função computed() para englobar uma arrow function que contém nossa lógica.

doubleCounter atualizando


Em resumo...

Exploramos as nuances de criar dados reativos no Vue, mergulhando na sintaxe da Options API e da Composition API. Ao desbravar a criação de propriedades de dados e computadas, tocamos na essência das Options API, onde a familiaridade com data properties e computed properties se revela crucial. Além disso, nos aventuramos na Composition API, onde ref, reactive e computed oferecem ferramentas poderosas para estruturar lógica reativa de forma mais modular e concisa.

Agora que você já sabe como declarar dados reativos, ampliando seu repertório para lidar com a reatividade no Vue e construir aplicações mais robustas e eficientes, você está pronto para elevar sua experiência de desenvolvimento Vue para novos patamares. Experimente, inove e desfrute da jornada reativa!

💖 💪 🙅 🚩
sucodelarangela
Angela Caldas

Posted on March 6, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related