Pular para conteúdo

Reatividade

Uma das características mais importantes do VueJS é a reatividade, que define que quando o estado de um objeto é alterado, os objetos que dependem desse estado são atualizados automaticamente. No VueJS, a reatividade é implementada através de um sistema de observação de dados. Assim, quando um dado é alterado, o VueJS atualiza automaticamente a interface do usuário.

De forma bem simplificada, podemos considerar que a reatividade é a forma de criar variáveis no VueJs. É fato que uma variável no VueJS pode ser criada usando os usuais comando let ou const, mas isso não é o suficiente para que o VueJS entenda que aquela variável é reativa. Para que o VueJS entenda que uma variável é reativa, é necessário que ela seja declarada usando a função reactive ouref, que são conhecidas como funções reativas.

Recapitulando, podemos dizer que a reatividade, no VueJS, é a forma de criar variáveis que, quando alteradas, o seu valor é atualizado automaticamente na interface do usuário, e vice-versa.

Outro conceito importante é o de propriedades computadas, que são funções que são executadas automaticamente quando uma variável reativa é alterada. Essas funções também serão apresentadas nesta aula.

Declarando variáveis reativas

Como vimos anteriormente, existem duas formas de declarar reatividade em um componente VueJS: reactive e ref. Nesta etapa, vamos estudar cada uma dessas formas. A primeira forma, reactive, embora seja muito comum não é a mais recomendada, embora seja interessante em alguns cenários. Por isso, neste curso, vamos focar no uso da função ref().

Declarando variável reativas usando ref

A função ref é a forma mais usual de declarar uma variável reativa. Ela recebe um valor como parâmetro e retorna um objeto que possui uma propriedade value que contém o valor passado como parâmetro. Veja o exemplo abaixo:

./src/App.vue
<script setup>
  import { ref } from 'vue';

  const contador = ref(0);

  function incrementar() {
    contador.value++;
  }
</script>

<template>
  <button @click="incrementar">{{ contador }}</button>
</template>

No exemplo acima, a variável contador é declarada usando a função ref. A variável contador é um objeto que possui uma propriedade value que contém o valor passado como parâmetro. Quando o botão é clicado, a função incrementar é chamada e o valor da propriedade value do objeto contador é incrementado. Como o objeto contador é reativo, o valor do botão é atualizado automaticamente.

Abaixo, alguns exemplos de declaração de variáveis reativas usando a função ref:

import { ref } from 'vue';
const nome = ref('João'); // string
const idade = ref(30); // number
const preco = ref(10.5); // number
const ativo = ref(true); // boolean
const frutas = ref(['maçã', 'banana']); // array de strings

O uso da função reactive

Embora seja possível declarar objetos reativos usando ref, a função reactive é mais recomendada para esse tipo de situação. Nesse caso, para acessar o valor do atributo não é necessário usar a propriedade value. Veja o exemplo do objeto pessoa com uma função para alterar a idade:

./src/App.vue
<script setup>
  import { reactive } from 'vue';

  const pessoa = reactive({
    nome: 'João',
    idade: 30,
    ativo: true,
  });

  function aniversario() {
    pessoa.idade++;
  }
</script>

<template>
  <div>
    <p>{{ pessoa.nome }} tem {{ pessoa.idade }} anos</p>
    <button @click="aniversario">Fazer aniversário</button>
  </div>
</template>

No exemplo acima, a variável pessoa é declarada usando a função reactive. A variável pessoa é um objeto que possui três propriedades: nome, idade e ativo. Quando o botão é clicado, a função aniversario é chamada e a propriedade idade do objeto pessoa é incrementada. Como o objeto pessoa é reativo, o valor da propriedade idade é atualizado automaticamente.

Uso nas templates e outras considerações

Embora já tenhamos apresentado exemplos anteriormente, note que para acessar o valor de uma variável reativa, é necessário usar simplesmente a sintaxe {{ variavel }, como qualquer variável. Mesmo no caso das variáveis definidas usando ref(), não é necessário acessar a propriedade value para acessar o valor da variável reativa, na parte de template.

Propriedades computadas

As propriedades computadas são funções que são executadas automaticamente sempre que uma de suas dependências é alterada. Elas são declaradas usando a função computed do VueJS. Veja o exemplo abaixo:

./src/App.vue
<script setup>
  import { reactive, computed } from 'vue';

  const professor = reactive({
    nome: 'Eduardo da Silva',
    disciplinas: [
      'Desenvolvimento Web II',
      'Desenvolvimento de Aplicativos para Dispositivos Móveis',
    ],
  });

  const possuiDisciplinas = computed(() => {
    return professor.disciplinas.length > 0;
  });
</script>

<template>
  <div>
    <h1>Professor</h1>
    <p>Nome: {{ professor.nome }}</p>
    <p v-if="!possuiDisciplinas">O professor não possui disciplinas.</p>
    <div v-else>
      <p>O professor possui disciplinas.</p>
      <ul>
        <li v-for="disciplina in professor.disciplinas" :key="disciplina">
          {{ disciplina }}
        </li>
      </ul>
    </div>
  </div>
</template>

Neste exemplo, alguns pontos importantes precisam ser destacados:

  • a propriedade computada possuiDisciplinas é declarada usando a função computed do componente VueJS.
  • a função computed recebe como parâmetro uma função que retorna o valor da propriedade computada.
  • possuiDisciplinas depende da propriedade disciplinas do objeto professor.
  • portanto, a propriedade computada possuiDisciplinas é executada automaticamente sempre que a propriedade disciplinas do objeto professor for alterada. Note que professor é um dado reativo, como visto anteriormente;

Ainda, o resultado da propriedade computada possuiDisciplinas é exibido na interface do usuário. Note que o valor da propriedade computada possuiDisciplinas é exibido na interface do usuário sempre que a propriedade disciplinas do objeto professor for alterada. Neste exemplo, o resultado será:

<h1>Professor</h1>
<p>Nome: Eduardo da Silva</p>
<div>
  <p>O professor possui disciplinas.</p>
  <ul>
    <li>Desenvolvimento Web II</li>
    <li>Desenvolvimento de Aplicativos para Dispositivos Móveis</li>
  </ul>
</div>

Note que se a propriedade disciplinas do objeto professor for alterada para um array vazio, o resultado da propriedade computada possuiDisciplinas será alterado automaticamente para Não, como o exemplo abaixo.

<h1>Professor</h1>
<p>Nome: Eduardo da Silva</p>
<p>O professor não possui disciplinas.</p>

Por que usar propriedades computadas e não métodos?

A principal diferença entre propriedades computadas e métodos é que as propriedades computadas são executadas automaticamente sempre que uma de suas dependências é alterada. Já os métodos são executados quando são chamados. Veja o exemplo abaixo, muito similar ao exemplo anterior.

./src/App.vue
<script setup>
  import { ref } from 'vue';

  const professor = ref({
    nome: 'Eduardo da Silva',
    disciplinas: [
      'Desenvolvimento Web II',
      'Desenvolvimento de Aplicativos para Dispositivos Móveis',
    ],
  });

  function possuiDisciplinas() {
    return professor.value.disciplinas.length > 0;
  }
</script>

<template>
  <div>
    <h1>Professor</h1>
    <p>Nome: {{ professor.nome }}</p>
    <p v-if="!possuiDisciplinas">O professor não possui disciplinas.</p>
    <div v-else>
      <p>O professor possui disciplinas.</p>
      <ul>
        <li v-for="disciplina in professor.disciplinas" :key="disciplina">
          {{ disciplina }}
        </li>
      </ul>
    </div>
  </div>
</template>

Neste exemplo, a propriedade computada possuiDisciplinas foi substituída por um método. O resultado ao executar o exemplo é o mesmo do exemplo anterior. Contudo, o resultado de um propriedade computada é mantido em cache, enquanto o resultado de um método é sempre recalculado. Em outras palavras, o resultado de uma propriedade computada é recalculado apenas quando uma de suas dependências é alterada. Já o resultado de um método é recalculado sempre que o método é chamado, o que no exemplo anterior acontecerá sempre que acontecer uma re-renderização da interface do usuário.

Mas porque isso é importante? Imagine que você tenham uma outra propriedade com um Array armazenado, não referente às disciplinas, mas aos projetos de um professor. Nesse caso, o resultado do método possuiDisciplinas será recalculado sempre que a interface do usuário for re-renderizada, por exemplo, ao adicionar um novo projeto mesmo que a propriedade disciplinas do objeto professor não tenha sido alterada. Isso pode causar um impacto negativo no desempenho da aplicação, pois o método possuiDisciplinas pode ser executado inúmeras vezes sem necessidade.

Exercícios

Para fixar o conteúdo apresentado, resolva os exercícios abaixo:

  1. Faça dois contadores (com botões de incrementar e decrementar). Mostre uma div com a soma dos dois valores. Também, usando propriedades computadas, faça uma div aparecer quando o valor da soma for maior que 10 e outra quando for menor que 10.
  2. Seguindo o mesmo exemplo anterior, faça uma div que seja apresentada se o resultado da soma for par e outra se a soma for ímpar. Também usando propriedades computadas.
  3. Faça uma variável reativa para guardar um valor booleano. Mostre uma div com o texto "Verdadeiro" quando o valor for verdadeiro e "Falso" quando o valor for falso.

Correção dos exercícios

Faremos a correção dos exercícios usando a API de composição. Vamos, além disso, corrigir todos os itens num só exemplo. Sugiro que você tente resolver os exercícios antes de ver a correção.

Correção dos exercícios
./src/App.vue
<script setup>
  import { computed, ref } from 'vue';

  const contador1 = ref(0);
  const contador2 = ref(0);
  const valorBooleano = ref(true);

  function incrementar(contador) {
    eval(contador).value++;
  }

  function decrementar(contador) {
    if (eval(contador).value > 0) {
      eval(contador).value--;
    }
  }

  const soma = computed(() => contador1.value + contador2.value);
  const somaPar = computed(() => soma.value % 2 === 0);
  const somaMaiorQue10 = computed(() => soma.value > 10);
</script>

<template>
  <h1>Correção dos exercícios</h1>
  <div class="booleano">
    <button @click="valorBooleano = !valorBooleano">
       {{ valorBooleano ? 'Esconder Resultado' : 'Mostrar Resultado' }}

    </button>
  </div>
  <div v-if="valorBooleano">
    <div class="contador">
      <h2>Contador 1</h2>
      <button @click="incrementar('contador1')">Incrementar</button>
      <button @click="decrementar('contador1')">Decrementar</button>
      <p>Valor: {{ contador1 }}</p>
    </div>
    <div class="contador">
      <h2>Contador 2</h2>
      <button @click="incrementar('contador2')">Incrementar</button>
      <button @click="decrementar('contador2')">Decrementar</button>
      <p>Valor: {{ contador2 }}</p>
    </div>
    <div class="soma">
      <h2>Soma</h2>
      <p>Valor:  {{ soma }} </p>
      <div v-if="somaMaiorQue10">
        <p>A soma é maior que 10</p>
      </div>
      <div v-else>
        <p>A soma é menor que 10</p>
      </div>
      <div v-if="somaPar">
        <p>A soma é par</p>
      </div>
      <div v-else>
        <p>A soma é ímpar</p>
      </div>
    </div>
  </div>
</template>

Note que esta é uma das formas de corrigir os exercícios. Existem outras formas de fazer a mesma coisa. O importante é entender o conceito e a sintaxe de templates do Vue 3.