En Vue.js, los composables son funciones que encapsulan lógica reactiva y puede ser reutilizada en múltiples componentes.
Los composables nos permiten organizar y reutilizar lógica de manera modular, lo que es especialmente útil en aplicaciones complejas.
Son una evolución de los mixins que existían en Vue.js 2, pero más flexibles y fáciles de mantener. Si veis mixins, es una sintaxis obsoleta
Sintaxis básica de un composable
Un composable es simplemente una función que utiliza las funciones de la Composition API, (como ref
, reactive
, computed
, y watch
).
Por convención, los composables se nombran empezando por ‘use’. Pero es simplemente una convención.
Por lo demás son funciones “normales”. Pueden devolver valores reactivos, métodos o cualquier otra lógica que necesitemos.
Vamos a ver un ejemplo, haciendo un composable que gestione el estado de un contador. Para ello creamos el fichero useCounter.js
, con este contenido
import { ref } from 'vue';
export function useCounter() {
const count = ref(0);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return {
count,
increment,
decrement,
};
}
En este ejemplo,
useCounter
es un composable que encapsula la lógica de un contador.- Devuelve un valor reactivo (
count
) y dos métodos (increment
ydecrement
).
Usar un composable en un componente
Ahora podemos usar nuestro composable en nuestro componente. Para ello, simplemente tenemos que importar la función y llámala dentro del setup
.
Por ejemplo, veamos como usar nuestro composable useCounter
en un componente.
<script setup>
import { useCounter } from './useCounter';
const { count, increment, decrement } = useCounter();
</script>
<template>
<div>
<p>Contador: {{ count }}</p>
<button @click="increment">Incrementar</button>
<button @click="decrement">Decrementar</button>
</div>
</template>
En este ejemplo,
- El componente utiliza el composable
useCounter
para manejar el estado del contador. - La lógica del contador está encapsulada en el composable, lo que hace que el componente sea más limpio y fácil de mantener.
Ejemplos prácticos de composables
Composable para manejar llamadas a API
Supongamos que queremos crear un composable para manejar llamadas a una API.
// useFetch.js
import { ref } from 'vue';
export function useFetch(url) {
const data = ref(null);
const loading = ref(true);
const error = ref(null);
async function fetchData() {
try {
const response = await fetch(url);
data.value = await response.json();
} catch (err) {
error.value = err;
} finally {
loading.value = false;
}
}
fetchData();
return {
data,
loading,
error,
};
}
En este ejemplo, useFetch
es un composable que maneja una llamada a una API. Devuelve los datos, el estado de carga y cualquier error que ocurra.
Usar el composable useFetch
en un componente
<template>
<div>
<p v-if="loading">Cargando...</p>
<p v-else-if="error">Error: {{ error.message }}</p>
<p v-else>Datos: {{ data }}</p>
</div>
</template>
<script setup>
import { useFetch } from './useFetch';
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts/1');
</script>
Composable para manejar el estado de un formulario
Supongamos que queremos crear un composable para manejar el estado de un formulario.
// useForm.js
import { ref } from 'vue';
export function useForm(initialState) {
const form = ref({ ...initialState });
function updateField(field, value) {
form.value[field] = value;
}
function resetForm() {
form.value = { ...initialState };
}
return {
form,
updateField,
resetForm,
};
}
En este ejemplo, useForm
es un composable que maneja el estado de un formulario. Devuelve el estado del formulario y métodos para actualizar y restablecer los campos.
Usar el composable useForm
en un componente
<template>
<form @submit.prevent="handleSubmit">
<input v-model="form.name" @input="updateField('name', $event.target.value)" placeholder="Nombre" />
<input v-model="form.email" @input="updateField('email', $event.target.value)" placeholder="Email" />
<button type="submit">Enviar</button>
<button type="button" @click="resetForm">Restablecer</button>
</form>
</template>
<script setup>
import { useForm } from './useForm';
const { form, updateField, resetForm } = useForm({ name: '', email: '' });
function handleSubmit() {
console.log('Formulario enviado:', form.value);
}
</script>