Tu primer Composable en Vue 3

El patrón de composición (composable) es una de las novedades más interesantes que nos trae la API de Composición de Vue 3. Nos permite usar la reactividad fuera de componentes, de forma sencilla. Algo impensable hasta ahora.


El patrón de composición (composable) es una de las novedades más interesantes que nos trae la API de Composición de Vue 3. Nos permite usar la reactividad fuera de componentes, de forma sencilla. Algo impensable hasta ahora.

De esta forma podemos componer pequeñas piezas lógicas y reutilizarlas en todo tu proyecto (u otros proyectos).

Esta es la definición “oficial” de composable sacada de la documentación de Vue.

Una función que utiliza la API de Composición para encapsular y reuitilizar lógica reactiva que mantiene su estado.

La mejor parte es que probablemente no tengas que aprender nada nuevo para utilizarlo, ya que si entiendes cómo funciona la Composition API, ya sabes cómo usar un composable.

Ahora bien, ¿cómo dar los primeros pasos con este patrón?. En mi experiencia he encontrado que la mejor manera es utilizar escenarios que conozcamos bien como zona de pruebas.

Un escenario familiar

Seguro que coincides conmigo: uno de los escenarios más comunes en desarollo Web (con Vue, por supuesto) es lanzar una petición HTTP, ¿verdad?. Algo así.

// AppComponent.vue
<script setup>
    import { ref } from 'vue';
    const results = ref(null);
    const API_URL = 'https://randomuser.me/api/?results=50';
    try {
      results.value = await (await fetch(API_URL)).json();
    } catch (err) {
      throw new Error(err.message);
    }
</script>

En realidad no hay nada demasiado interesante en el snippet. Usamos Async / Await para pedir un listado de usuarios y guardamos la respuesta en una referencia reactiva con ref. De igual forma, si algo no va bien capturamos el problema dentro del bloque catch y guardamos el error en otra referencia reactiva.

Tu primer composable

¿Cómo podemos crear un composable a partir de este escenario asíncrono?. Lo primero es elegir un nombre. Piensa en la funcionalidad o lógica que quieres abstraer. En este caso podría ser async o fetch, por ejemplo.

Usemos el primero para crear un composable llamado useAsync.

Vamos a crear un composable que gestione todo el ciclo de una petición HTTP de forma asíncrona por nosotros. Además, de forma reactiva.

Como ves la convención es prefijar el nombre de tus composables con la palabra use, reforzando la naturaleza práctica del propio patrón.

Entrada y salida

El segundo paso es pensar en términos de entrada y salida (IN & OUT), es decir, ¿qué información/parámetros (IN) necesita esta función compositora para hacer su trabajo?. En el caso de useAsync creo que basta con la URL con el endpoint donde realizar la petición.

Ahora, ¿qué necesitamos que nos devuelva (OUT) este composable para poder utilizarlo y que cumpla su misión?. En este caso el resultado de la petición y un error, si es que lo hay: result y error. Ambas serán referencias reactivas porque, recuerda, por primera vez podemos usar la reactividad de Vue (de forma mainstream) fuera de los componentes.

Teniendo claro lo que necesita y lo que retorna nuestro composable, vamos a crearlo.

JS
// composables/useAsync.js
import { ref, readonly } from 'vue';
export function useAsync() {
  const results = ref(null);
  const error = ref(null);
  const makeRequest = async (API_URL) => {
    try {
      const request = await (await fetch(API_URL)).json();
      results.value = request;
    } catch (err) {
      console.log(err);
      error.value = err;
    }
  };
  return {
    makeRequest,
    results: readonly(results),
    error,
  };
}

Utilizar un composable

El código coincide con la definición que te mostraba el principio de este DevTip: una función que utiliza la reactividad de Vue. Lo que estamos haciendo es exponer la función useAsync, la cual a su vez retorna una función que realiza las llamadas HTTP y acepta una API_URL. También retornamos las referencias reactivas con el resultado y posible error de la petición (result y error).

Ahora podemos realizar requests desde cualquier componente (o incluso otro composable) y estaremos reutilizando la misma lógica en lugar de duplicarla en diferentes partes del proyecto.

// AppComponent.vue
<script setup>
    import { useAsync } from '../composables/useAsync';
    const { makeRequest, results, error } = useAsync();
    await makeRequest('https://randomuser.me/api/?results=50');
</script>

Lo primero que hacemos es importar nuestro composable useAsync. Acto seguido ejecutamos la función que retorna para tener acceso a makeRequest, results y error.

Ahora solo nos queda lanzar la misma petición HTTP al endpoint. Todo lo demás debe funcionar exactamente igual que antes.

Teniendo en cuenta que error también es una referencia reactiva, si queremos reaccionar ante posibles problemas solo tenemos que observarlo.

// AppComponent.vue
<script setup>
    import { watchEffect } from 'vue';
    ...
    
    watchEffect(() => {
      if (error.value) alert('Problem found: ' + error.value.message);
    });
</script>

Conclusión

El patrón composable está aquí para quedarse, así que mi recomendación es que lo aprendas a utilizar desde ya. En este DevTip solo hemos visto lo esencial, pero más adelante iremos profundizando en otros aspectos como el manejo de estado ( stateful vs stateless).

Demo

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