El 90% de los composables Vue 3 tienen el mismo error
El 90% de los composables de Vue 3 que reviso en PRs tienen el mismo patrón.
Y no es culpa de nadie. La documentación de Vue es enorme y APIs como toValue() o toRaw() pasan desapercibidas hasta que aparece un bug difícil de reproducir.
Uno de los errores más comunes es hacer composables demasiado rígidos.
// ❌ Solo acepta Ref
export function useSearch(query: Ref<string>) {
const url = computed(() =>
`/api/search?q=${encodeURIComponent(query.value)}`
)
return { url }
}
Esto obliga a quien usa el composable a crear un ref, aunque ya tenga un valor o un getter.
Desde Vue 3.3 existe una forma mucho más flexible:
import { toValue, type MaybeRefOrGetter } from 'vue'
// ✅ Acepta valor, ref o getter
export function useSearch(query: MaybeRefOrGetter<string>) {
const url = computed(() =>
`/api/search?q=${encodeURIComponent(toValue(query))}`
)
return { url }
}
toValue() hace el trabajo por ti:
- Devuelve el valor si recibe un valor plano.
- Hace unwrap de un
ref. - Ejecuta un getter.
¿Y toRaw()?
No es una API para usar todos los días.
Úsala cuando realmente necesites sacar un objeto del sistema de reactividad, por ejemplo al integrarte con una librería externa que compara referencias, modifica objetos o no espera recibir proxies.
import { toRaw } from 'vue'
const raw = toRaw(state)
externalLibrary.process(raw)
Mi checklist antes de aprobar un composable
- ✅ Devuelve
refycomputedcuando tenga sentido. - ✅ Si un parámetro puede ser un valor o un
ref, usaMaybeRefOrGetter+toValue(). - ✅ Recurre a
toRaw()solo cuando realmente necesitas salir de la reactividad.
Son pequeños detalles, pero hacen que los composables sean mucho más reutilizables y evitan bugs muy difíciles de localizar.