Ir al contenido
wannacode

Actualizar objetos y arrays

En resumen: Un objeto o un array en el estado de Vue se cambia en el sitio, porque Vue vigila los cambios mediante un proxy; un array nuevo en .value también funciona, pero un array normal sin ref no.

Problema

«Quitar» no quita nada

El carrito tiene tres líneas, cada una con un botón «Quitar». Pulsa «Quitar» en el pan.

Vue

La consola dice líneas: 2. La pantalla muestra las tres líneas y el total viejo. Los datos cambiaron, la pantalla no los sigue, y no hay ningún error.

remove pone en cart un array nuevo sin el pan. El valor nuevo está en la variable. ¿Por qué Vue no dibuja?

La idea

Cómo ve la biblioteca un cambio

Con un número está claro: 1 y 2 son distintos. Un array y un objeto, en cambio, son referencias: la variable apunta a un objeto en la memoria. Hay dos preguntas. ¿La variable apunta a otro objeto? ¿Y cambió el contenido del mismo objeto? Cada biblioteca pregunta a su manera, y aquí la diferencia se nota en el código.

Vue hace la segunda pregunta. ref([...]) envuelve el array en un proxy: un objeto que se pone delante del array y ve cada lectura y cada escritura. push, splice y line.qty++ pasan por el proxy. Él registra el cambio y avisa a la plantilla que lee el array para que se ejecute otra vez. Por eso en Vue el array se cambia en el sitio, y es la forma normal.

Pero el proxy no ve la variable que apunta a él. En el ejemplo let cart = reactive([...]) y después cart = cart.filter(...): la variable apunta ahora a un array nuevo y normal. La plantilla lee el array viejo mediante el proxy viejo, y nadie le avisa. Con const cart = ref([...]) el array nuevo va a cart.value, y la ref ve ese cambio.

Míralo

Mira: el mismo array o uno nuevo

<script setup>
const cart = ref([bread]);
function addMilk() {
cart.value.push(milk);
}
function addCheese() {
cart.value = [...cart.value, cheese];
}
</script>
Paso a paso, en palabras
  1. cart es una ref. Dentro está el array, envuelto en un proxy: un objeto que se pone delante del array y ve cada lectura y cada escritura.
  2. cart.value.push(milk) pasa por el proxy. El proxy registra: el array cambió.
  3. El proxy avisa a la plantilla, que lee cart, de que tiene que ejecutarse otra vez.
  4. La plantilla se ejecuta y la pantalla muestra las dos líneas.
  5. Así también vale: un array nuevo en cart.value. La ref nota el cambio de .value.
  6. La ref avisa a la plantilla, y la plantilla se ejecuta otra vez con el array nuevo.
  7. La pantalla muestra tres líneas. En Vue funcionan los dos caminos.
  8. La regla en Vue: cambia a través de la ref. Vue no ve una variable normal ni la copia de un número.
Los dos caminos pasan por la ref: push por el proxy del array y el array nuevo por .value. Vue se pierde solo lo que pasa por fuera de los dos.

La idea

Un cambio a través del proxy

El carrito tiene tres acciones: añadir una línea, cambiar la cantidad de una línea, quitar una línea.

// añadir
cart.value.push({ ...product, qty: 1 });
// cambiar una línea: la línea también es un proxy
const line = cart.value.find((l) => l.id === id);
line.qty++;
// quitar
const index = cart.value.findIndex((l) => l.id === id);
cart.value.splice(index, 1);
  • Vue vigila también los objetos dentro del array: line de cart.value es un proxy y line.qty++ se ve.
  • El otro camino también funciona: un array nuevo en cart.value, por ejemplo cart.value = cart.value.filter((l) => l.id !== id). La ref ve el cambio de .value.
  • Un cambio que pasa por fuera de la ref no se ve: una variable normal con un array nuevo, o una copia de un número.

Pruébalo

Arregla «Quitar»

Este es el carrito del principio. Arregla remove para que la línea desaparezca y el total se calcule de nuevo. La comprobación quita el pan y después el queso.

Vue

Pruébalo

Encuentra el error

«+» no sube la cantidad. ¿Qué línea tiene la culpa?

Elige la línea con el error.

La idea

Una línea del array

La clienta pulsa «+» en el pan. Tiene que cambiar el qty de un objeto, y el objeto está en un array.

En Vue line.qty++ es la forma correcta: la línea de cart.value es un proxy y Vue ve el cambio. El error es sacar el número del objeto:

let { qty } = line; // una copia del número
qty++; // cambia la copia

La desestructuración y let n = line.qty dan una copia. Vue vigila el objeto line, no cada variable por la que ha pasado el número. Escribe en el objeto: line.qty++.

Más precisamente, para curiosos

Vigilancia profunda. ref y reactive hacen que también se vigilen los objetos de dentro, la primera vez que se leen. shallowRef vigila solo .value: con ella no se ve un cambio interno, y pones un array nuevo, como en React.

reactive y la desestructuración. const { total } = reactive({ total: 0 }) da un número, no un valor vigilado. toRefs(obj) convierte los campos en refs que siguen conectadas al objeto.

El array nuevo. cart.value = [...cart.value, line] funciona y hace un array nuevo, como harías en React. Es útil cuando el array viejo tiene que quedar igual, por ejemplo para «Deshacer».

structuredClone no acepta un proxy: structuredClone(cart.value) lanza un error. Primero saca el array normal con toRaw(cart.value).

Pruébalo

«+» y «−»

Escribe more y less. «+» sube la cantidad de la línea en 1, y «−» la baja en 1. Cuando la cantidad es 1, «−» quita la línea. El total sigue a la cantidad.

Vue

Ejercicio

Catálogo y carrito

El catálogo tiene cuatro productos con un botón «Añadir» (el nombre para el lector de pantalla: «Añadir Pan blanco»). Escribe add y remove. El primer clic en «Añadir» hace una línea con cantidad 1, y los siguientes suben la cantidad de la misma línea en lugar de hacer una nueva. «Quitar» quita la línea. Cuando el carrito está vacío, dice «El carrito está vacío».

Vue

Test

Ponte a prueba

1. let cart = reactive([...]); y después cart = cart.filter(...). ¿Por qué no cambia la pantalla?
2. ¿Cuál sube bien la cantidad de una línea?
3. ¿Funciona cart.value = [...cart.value, line] en Vue?
4. const cart = [ ... ] sin ref, y después cart.push(line). ¿Qué pasa?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·