API de Reactividad (Vue 3)

Quizás, uno de los cambios principales de Vue 3 que encontraremos a la hora de escribir código en nuestros componentes, es que ahora el manejo de la reactividad se vuelve algo más «explícito». En primer lugar, en Vue 3 tenemos varios métodos que podemos utilizar para crear datos reactivos a partir de variables u objetos:

Método Observaciones
ref() Devuelve el tipo primitivo o como un objeto reactivo.
reactive() Devuelve el (no primitivo) como un objeto reactivo.
readonly() Devuelve el (o dato reactivo) como un objeto que no puede mutar.
isRef() Comprueba si una variable es una referencia reactiva a un dato primitivo.
isReactive() Comprueba si una variable es un objeto reactivo.
isReadonly() Comprueba si una variable es un objeto inmutable de sólo lectura.
isProxy() Comprueba si una variable es un proxy creado por reactive o readonly.
shallowRef() Idem a ref: Vigila mutaciones, pero no devuelve su valor como reactivo.
shallowReadonly() Idem a readonly, pero sólo de forma superficial (no afecta a sus hijos).
shallowReactive() Idem a reactive, pero sólo de forma superficial (no afecta a sus hijos).

Veamos como podemos gestionar la reactividad en esta nueva etapa de Vue, utilizando algunas de las funciones de esta tabla y otras relacionadas con la nueva API de reactividad.

Reactividad de Vue 3

La función ref

En primer lugar, importaremos la función ref desde vue. Esta función sólo es válida para tipos primitivos (números, strings o booleanos), y como vemos en el siguiente ejemplo, podemos utilizarla para crear una variable productPrice que será una referencia reactiva inicializada con el valor de la prop price.

De forma paralela, crearemos una función add que se encargará de aumentar el valor cada vez que se pulse sobre el texto del precio. Devolvemos ambas al componente, para utilizarlas en la parte del <template>:

<template>
  <div @click="inc">{{ productPrice }}</div>
</template>

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

export default {
  props: {
    price: Number
  },
  setup() {
    const productPrice = ref(props.price);
    const add = () => productPrice.value++;

    return { productPrice, add }
  }
}
</script>

Observa que para aumentar el valor de la variable reactiva, hemos accedido a la propiedad .value, ya que ref crea un objeto reactivo que tendrá la propiedad value, donde almacenará su contenido. Algo similar a ejecutar una función como la que ves en el siguiente ejemplo:

function ref(primitiveValue) {
  return {
    value: primitiveValue
  };
}

Ten en cuenta este detalle, ya que tendrás que acceder a dicha propiedad value para acceder a su información, aunque en la parte de <template> no es necesario y puedes acceder al objeto reactivo devuelto directivamente.

En el caso de que se le pase a la función ref() un valor no primitivo (objeto, por ejemplo), internamente Vue se lo pasará a la función reactive, que veremos a continuación.

La función reactive

La función reactive realiza el mismo trabajo para objetos no primitivos. Mientras que la función ref la utilizabamos para convertir en datos reactivos las variables o datos primitivos, la función reactive se utiliza para hacer lo mismo con tipos de datos más complejos (objetos, arrays, etc...).

Observa que si intentamos hacer el mismo ejemplo anterior utilizando reactive() en lugar de ref(), si lo hacemos con un valor primitivo (por ejemplo, un 0), obtendremos el siguiente error:

value cannot be made reactive: 0

Como hemos dicho previamente, en lugar de ref(), utilizaremos la función reactive(), que nos permitirá crear datos reactivos a partir de objetos Javascript:

<template>
  <div @click="inc">{{ productPrice }}</div>
</template>

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

export default {
  props: {
    price: Number
  },
  setup(props) {
    const productPrice = reactive({
      name: "Product Name",
      price: props.price
    });
    const add = () => productPrice.price++;

    return { productPrice, add }
  }
}
</script>

Observa que en este caso, no accedemos a una propiedad .value, sino que con reactive() directamente devolvemos el objeto (y todas sus propiedades hijas) como datos reactivos.

La función readonly

Por último, tenemos la función readonly, que viene a ser la opuesta a las anteriores. Mientras que ref y reactive convierten una variable en reactiva, readonly convierte una variable (normal o reactiva) en una versión inmutable que no se podrá variar. Al igual que las anteriores (y a diferencia de las versiones shallow*), afectará a todos sus hijos.

Computed / Watch

Al igual que en la Options API, en la nueva Composition API también tenemos propiedades computadas y watchers. Sin embargo, también han cambiado ligeramente en su forma de declararlas. A modo de resumen, tenemos los siguientes métodos:

Método Observaciones
computed Propiedad computada. Cachea información mientras sus dependencias reactivas no cambien.
watchEffect Ejecuta una función y la vuelve a ejecutar cada vez que sus dependencias reactivas cambien.
watch Función asociada a una variable reactiva y que será ejecutada cada vez que esta haya mutado.

La función computed()

Con la función computed() podemos crear las propiedades computadas que creabamos en Vue 2 en el objeto computed, simplemente importando la función de vue y pasándole una función anónima (o callback) que será la que devuelva el contenido de la propiedad computada.

Recuerda que una propiedad computada devuelve un valor precalculado que se mantiene cacheado mientras sus dependencias reactivas no hayan cambiado, de modo que son muy interesantes para guardar resultados que pueden ser costosos y no tener que realizarlos varias veces. En Vue 3 lo definiríamos así:

<template>
  <div @click="inc">{{ offerPrice }}</div>
</template>

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

export default {
  props: {
    price: Number
  },
  setup(props) {
    const productPrice = ref(props.price);
    const offerPrice = computed(() => `¡Oferta!: ${productPrice.value / 2}`)
    const add = () => productPrice.value++;

    return { offerPrice, add }
  }
}
</script>

Observa que en este ejemplo, estamos generando una variable productPrice que es reactiva y que obtiene su valor de la prop price que le llega al componente. En la línea siguiente, creamos una propiedad computada offerPrice que nos devuelve un texto de oferta del 50% (dividido entre dos), y que solo se va a recalcular cuando cambie su dependencia reactiva, es decir, cuando cambie el valor de la variable productPrice.

Al final, devolver las variables offerPrice y la función add() que son las que vamos a utilizar en el <template>.

Las funciones watchEffect y watch

Mediante la función watchEffect() podemos crear una función que se ejecutará inmediantamente, y luego comenzará a vigilar las dependencias reactivas que tiene, volviéndose a ejecutar cada vez que cambien.

Por otro lado, tenemos la función watch donde hace algo muy similar, pero con un grado de precisión más fino. La función watch() nos permite indicar que dependencia reactiva concretamente vamos a vigilar, así como obtener el valor previo y posterior de la variable reactiva que ha cambiado, por ejemplo.

<template>
  <div @click="inc">{{ offerPrice }}</div>
</template>

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

export default {
  props: {
    price: Number
  },
  setup(props) {
    const productPrice = ref(props.price);

    // Ejecuta inmediatamente, y cada vez que cambien sus dependencias reactivas
    watchEffect(() => console.log(`watchEffect => `, productPrice.value));

    // Ejecuta la función cuando cambia la variable reactiva productPrice.
    // La función callback puede obtener el valor actual (current) y anterior (prev).
    watch(productPrice, (current, prev) => {
      console.log(`watch => current: ${current} prev: ${prev}`);
    });

    const add = () => productPrice.value++;

    // Ejecuta la función add (suma 1) cada 3 segundos
    setInterval(add, 3000);

    return { offerPrice, add }
  }
}
</script>

Estas son algunas de las características que cambian en Vue 3 en su nueva API de reactividad, mediante la cuál podemos trabajar de forma mucho más limpia con variables o datos reactivos, sin estar atados a la API de opciones de Vue 2 y posteriores.

Manz
Publicado por Manz

Docente, divulgador informático y freelance. Autor de Emezeta.com, es profesor en la Universidad de La Laguna y dirige el curso de Programación web FullStack y Diseño web FrontEnd de EOI en Tenerife (Canarias). En sus ratos libres, busca GIF de gatos en Internet.