Top 3 errores trabajando con la Composition API

Tres errores comunes al usar la API de Composición en Vue.js, explicando qué son, por qué ocurren y cómo solucionarlos


Te guste o no, la API de Composición es el nuevo estándar de Vue.js. Como ocurre con cualquier novedad importante, ya sea por falta de atención o de documentación, su adopción puede llevar a cometer errores que con el tiempo se convierten en malas prácticas o ejemplos de uso incorrecto.

En este DevTip, examinaremos tres de estos errores comunes: cuáles son, por qué ocurren y cómo puedes solucionarlos de una vez por todas.

Problema 1: Uso incorrecto de reactive

Este es un clásico y uno de los motivos por los que algunos desarrolladores Vue prefieren usar siempre ref, en lugar de reactive.

Primero, no podemos usar valores primitivos con reactive, solo objetos como Object, Array, Map y demás. Segundo, una vez creada la referencia, no se puede romper. Esto quiere decir que esa referencia no debe cambiar, pero sí mutar.

Por último, y como consecuencia de ello, no se puede desestructurar reactive sin un poco de ayuda extra. Me explico:

TS
import { reactive } from 'vue';
const state = reactive({
  count: 0,
  name: 'John',
});
const { name } = state;
state.name = 'Juan';
console.log(name, state.name); // John Juan

Como ves, una vez realizamos la desestructuración se pierde la referencia y por ende name no se actualiza.

Para desestructurar reactive puedes usar el helper toRefs, que convertirá las propiedades un objeto creado con reactive en referencias reactivas (generadas con ref).

TS
import { reactive, toRefs } from 'vue';
const state = reactive({
  count: 0,
  name: 'John',
});
const { name } = toRefs(state);
state.name = 'Juan';
console.log(name.value, state.name); // Juan Juan

Como ves, ahora name es una referencia reactiva enlazada al objeto reactivo. En realidad ahora es un objeto ya que se accede a través de value. Mientras esto siga siendo así, siempre se mantendrán sicronizadas.

Problema 2: Demasiada lógica en tus componentes

Uno de los aspectos más positivos de la Options API es que nos fuerza a estructurar la lógica en diferentes partes. Gracias a ella, tu código resulta más organizado. Al dar el paso a la Composition API, nuestro código puede parecer desorganizado, ya que ahora tenemos mayor libertad. Ahora cae en nosotros la responsabilidad de seguir un patrón de estructura de código.

Otro (bajo mi punto de vista) error es agrupar un montón de lógica relacionada en un componente. Muchas de las veces olvidamos que ahora podemos usar las API reactivas (ref, reactive y demás) fuera de ellos.

Un ejemplo:

TS
import { ref, reactive, onMounted, watch } from 'vue';
const data = ref(null);
const error = ref(null);
const searchTerm = ref('');
const state = reactive({ count: 0 });
const fetchData = async () => {
  try {
    const response = await fetch(
      `https://api.example.com/data?search=${searchTerm.value}`
    );
    data.value = await response.json();
  } catch (err) {
    error.value = err;
  }
};
onMounted(fetchData);
watch(searchTerm, fetchData);

No hay nada negativo en el código de arriba, pero en la mayoría de ocasiones es interesante extraer funciones y lógica que pueda ser reutilizada en otras partes. En este caso, la función fetchData se convierte en el composable useFetchData:

TS
import { ref, onMounted, watch } from 'vue';
export function useFetchData(searchTerm) {
  const data = ref(null);
  const error = ref(null);
  const fetchData = async () => {
    try {
      const response = await fetch(
        `https://api.example.com/data?search=${searchTerm.value}`
      );
      data.value = await response.json();
    } catch (err) {
      error.value = err;
    }
  };
  onMounted(fetchData);
  watch(searchTerm, fetchData);
  return { data, error };
}

Utilizarlo es tan sencillo como:

TS
import { useFetchData } from '@/composables';
const searchTerm = ref('');
const { data, error } = useFetchData(searchTerm);

Problema 3: Esperar reactividad cuando no es posible

Por último, pero no menos importante, tenemos aquellas situaciones en las que se espera que algo siga siendo reactivo cuando en realidad ya no lo es. Esta falsa asunción hace que este tipo de errores sea los más difíciles de depurar.

Supongamos que queremos pasar parte de las propiedades reactivas de un componente a otro lugar —como un composable— y esperar que sigan siendo reactivas.

Un ejemplo supersimplificado:

TS
import { watch } from 'vue';
export function useTest(msg) {
  watch(
    msg,
    (newVal) => {
      console.log(newVal);
    },
    { immediate: true }
  );
}

Ahora hacemos uso del composable useTest en nuestro componente:

Vue
<script setup>
  import { useTest } from '@/composables/useTest'
  const props = defineProps<{
    msg: string
  }>()
  useTest(props.msg)
</script>

¿Ves el problema?. A simple vista no es sencillo.

El problema es que props.msg no es una referencia reactiva, es el resultado de ella. El valor final. Igual que si hicieras referencia a la propiedad value de algo creado con ref. Lo que estás pasando a useTest es un string, no un objeto.

De hecho, si intentas poner en uso el código de arriba Vue mismo te dará un mensaje de advertencia:

Vue warn]: Invalid watch source: You did it! A watch source can only be a
getter/effect function, a ref, a reactive object, or an array of these types.

¿Qué podemos hacer al respecto?. En realidad ya lo sabes: toRefs. De nuevo. De esta forma creas una referencia reactiva (de nuevo, con ref) usando el valor de props.msg:

Vue
<script setup lang="ts">
  import { toRefs } from "vue"
  ...
  const { msg } = toRefs(props)
  useTest(msg)
</script>

Ahora todo funciona como te imaginas, y cualquier cambio en props.msg desatará la reactividad, siendo useTest notificado de la mutación.

Conclusiones

En resumen, el uso de la API de Composición en Vue.js puede ser un gran cambio, pero también puede aportar muchos beneficios en términos de flexibilidad y capacidad para reutilizar lógica. Sin embargo, como con cualquier herramienta, es importante usarla correctamente para evitar caer en trampas comunes y errores.

Al entender cómo manejar adecuadamente los objetos reactivos, estructurar tu código de manera eficiente y manejar la reactividad, puedes aprovechar al máximo esta poderosa característica de Vue.js.

Si estás interesado en aprenderla, dale un vistazo al Taller de Composition API.

Juan Andrés Núñez
Juan Andrés Núñez
Ingeniero Frontend. Especialista en Vue.js. Docente profesional. Estoico.