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.
// 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).