Composables en Vue 3: cómo extraer lógica reutilizable sin complicarte
Hay una señal bastante clara de que tu componente está pidiendo ayuda: empiezas a copiar la misma lógica en dos, tres o cuatro lugares.
Primero fue un menú que se abre y se cierra. Luego un modal. Después un panel lateral. Cada uno tiene su propio ref, su función para abrir, su función para cerrar y un toggle casi idéntico. No pasa nada grave al principio, pero el código empieza a sentirse repetido, como si cada componente estuviera resolviendo el mismo problema por su cuenta.
Ahí entran los composables.
Qué es un composable, sin hacerlo raro
Un composable es una función que encapsula lógica reutilizable usando la Composition API de Vue.
Nada más. No necesita sonar más grande que eso.
Puede guardar estado con ref() o reactive(), crear valores derivados con computed(), escuchar cambios con watch() o conectar con APIs del navegador. La idea es que esa lógica viva en un lugar claro y pueda usarse desde varios componentes sin copiar y pegar.
Si un componente debería encargarse de mostrar la interfaz, un composable puede encargarse de una parte de la lógica que la alimenta.
El problema: lógica repetida dentro del componente
Imagina un menú pequeño:
<script setup>
import { ref } from 'vue'
const isOpen = ref(false)
const open = () => {
isOpen.value = true
}
const close = () => {
isOpen.value = false
}
const toggle = () => {
isOpen.value = !isOpen.value
}
</script>
<template>
<button @click="toggle">
{{ isOpen ? 'Cerrar menu' : 'Abrir menu' }}
</button>
<nav v-if="isOpen">
Menu visible
</nav>
</template>Este código está bien. No hay que extraer todo apenas aparece una función. El problema llega cuando la misma idea aparece en un modal, un acordeón, una tarjeta expandible y un panel de filtros.
En ese punto ya no estás escribiendo lógica del menú. Estás escribiendo una pequeña regla reutilizable: algo puede estar activo o inactivo, y necesitas abrirlo, cerrarlo o alternarlo.
El después: extraer un useToggle
Podemos llevar esa lógica a un composable:
import { ref } from 'vue'
export function useToggle(initialValue = false) {
const isActive = ref(initialValue)
const open = () => {
isActive.value = true
}
const close = () => {
isActive.value = false
}
const toggle = () => {
isActive.value = !isActive.value
}
return {
isActive,
open,
close,
toggle
}
}Y ahora el componente queda más enfocado:
<script setup>
import { useToggle } from '@/composables/useToggle'
const menu = useToggle()
</script>
<template>
<button @click="menu.toggle()">
{{ menu.isActive ? 'Cerrar menu' : 'Abrir menu' }}
</button>
<nav v-if="menu.isActive">
Menu visible
</nav>
</template>El componente todavía se entiende. De hecho, se entiende mejor: hay un menú y ese menú se puede alternar. La mecánica interna ya no estorba.
Cuándo sí vale la pena crear un composable
Un composable tiene sentido cuando extrae una intención real, no solo unas líneas de código.
Suele valer la pena cuando:
- La misma lógica aparece en más de un componente.
- El componente está mezclando demasiadas responsabilidades.
- Quieres probar o razonar una parte del comportamiento por separado.
- La lógica tiene un nombre claro:
useToggle,useMousePosition,useLocalStorage,usePagination.
La clave está en el nombre. Si puedes nombrarlo de forma natural, probablemente hay una idea reutilizable ahí.
Cuándo no hace falta
No todo necesita convertirse en composable.
Si una lógica solo vive en un componente y se entiende bien ahí, déjala ahí. Extraer demasiado pronto también complica. Terminas saltando entre archivos para entender algo que antes se leía en veinte segundos.
Un buen composable reduce ruido. Uno innecesario lo mueve de lugar.
Errores comunes
El primero es crear nombres demasiado vagos. useHelpers, useUtils o useCommon no dicen nada. Si el nombre no explica la intención, el archivo se convierte en un cajón de cosas sueltas.
El segundo es meter demasiada lógica en el mismo composable. useUserDashboardEverything puede empezar cómodo, pero pronto será más difícil de mantener que el componente original.
El tercero es esconder efectos secundarios. Si un composable hace una petición, escribe en localStorage o registra eventos globales, debería ser evidente por su nombre o por cómo se usa.
El cuarto es devolver demasiadas cosas. Si un composable devuelve quince propiedades, quizá todavía no encontraste el límite correcto.
Una regla práctica
Antes de crear un composable, pregúntate esto:
¿Estoy extrayendo una idea o solo estoy moviendo código?
Si estás extrayendo una idea, adelante. Si solo estás moviendo código para que el componente se vea más corto, espera un poco.
Los composables no están para lucirse. Están para que el código respire mejor, para que los componentes hablen de interfaz y para que la lógica repetida tenga una casa propia.
