Cómo desestructurar reactive sin perder la reactividad

Por defecto no puedes usar el operador spread para desestructurar objetos reactivos creados con Vue 3 reactive API. En este tip te explicaré cómo usar los helpers toRef y toRefs para poder desestructurar reactive sin perder la reactividad


Si en tus proyectos Vue trabajas con la Composition API (algo que te recomiendo), habrás utilizado reactive para crear objetos reactivos. Por ejemplo, algo así:

JS
const state = reactive({
    online: true,
    username: 'juanwmedia'
})

Ahora bien, ¿qué ocurre si quieres crear una referencia reactiva (con ref) a una propiedad del objeto reactivo state?.

Quizás pienses que puedes acceder a él sin más y mantener la reactividad:

JS
const username = ref(state.username) // ❌ no es reactivo

Sin embargo, esto no será reactivo, ya que lo estaremos enviando a ref es un valor primitivo (en este caso un string), no la referencia al objeto reactivo que lo contiene.

toRef al rescate

Para poder crear una referencia reactiva a una propiedad de un objeto reactivo, Vue pone a nuestra disposición toRef. Utilizarlo es muy sencillo:

JS
const username = toRef(state, 'username') // ✅ es reactivo

Como ves, toRef acepta dos argumentos. El primero es el objeto reactivo donde vive la propiedad que queremos referenciar. Por último, el identificador de la propiedad en sí.

Otro ejemplo típico donde es muy sencillo perder reactividad es trabajando con funciones compositoras, más conocidas como composables.

Supongamos que nuestro composable devuelve un objeto reactivo. Por ejemplo, las coordenadas del ratón:

JS
export const useMouse = () => {
    const coords = reactive({x: 0, y: 0});
    function updateCoords({pageX, pageY}) {
        console.log(pageX, pageY);
        coords.x = pageX;
        coords.y = pageY;
    }
    onMounted(() => window.addEventListener('mousemove', updateCoords));
    onUnmounted(() => window.removeEventListener('mousemove', updateCoords));
    return coords; // 👁 retornamos un objeto reactivo
};

Cuando ejecutemos el composable, es muy tentador desestructurar el objeto resultante:

JS
const {x, y} = useMouse(); // ❌ perdemos la reactividad

Sin embargo, recuerda, no estamos trabajando con un objeto plano sino con un reactivo (con Proxy). No podemos extraer un valor sin más y mantener la reactividad.

toRefs

Para solucionar el problema tenemos otra función llamada toRefs, esta convierte un objeto reactivo en uno plano, donde cada propiedad es una referencia reactiva (creada con toRef) a la propiedad original del objeto reactivo.

En nuestro ejemplo, podemos aplicar toRefs en dos lugares. El primero es al hacer uso del composable.

JS
const {x, y} = toRefs(useMouse()); // ✅ mantenemos la reactividad

O —más útil— retornar toRefs desde el propio composable.

JS
export const useMouse = () => {
...
    return toRefs(coords);
};

Demo

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