Ir al contenido
wannacode

Estado

En resumen: El estado es un valor que un componente recuerda entre renders; en Vue se guarda con ref, y cada cambio actualiza el componente en la página.

Problema

El botón no cuenta

La tarjeta del producto tiene un botón «Añadir» y un contador «En el carrito». Pulsa «Añadir» tres veces.

Vue

La consola dice count = 1, 2, 3. La tarjeta dice 0. No hay ningún error. La variable count cambia, pero la pantalla no la sigue. ¿Dónde está la diferencia?

La idea

Una variable normal no basta

La tarjeta no la dibuja tu código, sino la biblioteca. Solo vuelve a dibujar cuando sabe que algo cambió. count++ cambia la variable, pero no avisa a nadie.

<script setup> se ejecuta una vez por cada tarjeta, y la plantilla la ejecuta Vue. Vue vuelve a ejecutar la plantilla solo cuando cambia algo que vigila. Vue no vigila una variable let normal. El clic cambia count, pero la plantilla no se vuelve a ejecutar, así que la tarjeta se queda en 0.

Necesitas datos que el componente recuerde entre dos renders y cuyo cambio lo vuelva a dibujar. Esto se llama estado.

La idea

useState y ref

<script setup>
import { ref } from 'vue';
const count = ref(0);
function add() {
count.value++;
}
</script>
<template>
<p>En el carrito: {{ count }}</p>
</template>
  • ref(0) devuelve una caja, y el número está en count.value. En el script escribes .value.
  • En la plantilla Vue desenvuelve la caja: escribes {{ count }}, sin .value.
  • Cuando la plantilla lee count, Vue lo recuerda. Cuando count.value cambia, Vue vuelve a ejecutar la plantilla.

El manejador del clic se da como función: @click="add". En Vue también funcionan @click="add()" o @click="count++": la plantilla los envuelve en una función que se ejecuta al hacer clic.

Vue

Míralo

Mira qué pasa con un clic

<script setup>
const count = ref(0);
function addThree() {
count.value++;
count.value++;
count.value++;
}
</script>
<template>
<article>
<button @click="addThree">+3</button>
<p>En el carrito: {{ count }}</p>
</article>
</template>
Paso a paso, en palabras
  1. La tarjeta guarda count en una ref: una caja cuyo valor es count.value. Vue vigila quién la lee.
  2. Vue ejecuta la plantilla. Lee count y devuelve una descripción: un botón y un texto con 0. Vue recuerda que la plantilla lee count.
  3. La primera vez, la descripción se convierte en elementos reales en el DOM. La página ya muestra «En el carrito: 0».
  4. Un clic en +3: el navegador llama a addThree.
  5. Tres veces count.value++. La caja cambia al instante: 1, 2, 3. Vue planea ejecutar la plantilla otra vez, pero solo una.
  6. El manejador terminó. Vue ejecuta la plantilla una vez, con count = 3, y devuelve una descripción nueva.
  7. Vue compara la descripción nueva con la vieja. El botón es el mismo, solo cambió el texto. En el DOM cambia solo el texto.
  8. El contador muestra 3. El cambio de una ref es inmediato, y el dibujo ocurre una vez, después del manejador.

La tarjeta no se construye de nuevo. La biblioteca compara la descripción nueva con la vieja y cambia en el DOM solo la diferencia: el texto. El botón es el mismo elemento y el foco se queda en él. Esa es la diferencia con render() del módulo del DOM, que lo reemplazaba todo.

Pruébalo

Arregla el contador

Esta es la tarjeta del principio. Haz que count sea estado, para que después de cada clic en «Añadir» la tarjeta muestre el número nuevo.

Vue

La idea

La caja y el número

El agua mineral también se vende en un pack de 6 botellas. Sería cómodo que el botón del pack llamara seis veces a add(). Aquí las dos bibliotecas se comportan de forma distinta.

count.value es siempre el número actual, así que seis veces count.value++ da 6. La plantilla no se ejecuta seis veces por eso. Vue junta los cambios y la ejecuta una vez, después del manejador.

Aquí el error es otro. La caja es count, y count.value es un número. Un número puesto en otra variable es una copia:

let bottles = count.value;
bottles += 6; // cambia la copia, count queda igual

Vue vigila la caja, no cada variable por la que ha pasado el número. Escribe en la caja: count.value += 6.

Pruébalo

Encuentra el error

«Añadir un pack» debería añadir 6 botellas. ¿Qué línea tiene la culpa?

Elige la línea con el error.

Pruébalo

Un pack de agua

Escribe addBox() para que «Añadir un pack (6 uds.)» sume 6 botellas a lo que ya hay en el carrito. La comprobación pulsa «Añadir» una vez, luego el pack dos veces, y espera 1, 7 y 13.

Vue

La idea

Cada tarjeta recuerda lo suyo

El estado pertenece a la instancia, no al código. El catálogo tiene dos tarjetas del mismo ProductCard, y cada una tiene su propio count:

Vue

Pulsa «Añadir» dos veces en el pan y una en la leche: 2 y 1. La biblioteca guarda el estado de cada tarjeta según su lugar en el árbol de componentes. Si quitas la tarjeta de la página, su estado se va con ella. Cuando dos tarjetas tienen que mostrar el mismo número, este tiene que vivir en su padre común. De eso trata la lección sobre estado compartido de este módulo.

Más precisamente, para curiosos

ref y reactive. reactive({ count: 0 }) hace que el objeto se vigile y no tiene .value, pero solo funciona con objetos y no se puede reemplazar el objeto entero. ref funciona con cualquier valor, por eso la documentación de Vue la recomienda como la principal.

Cuándo se ejecuta la plantilla. Vue vuelve a ejecutar la plantilla en la siguiente microtarea, cuando termina el código que se está ejecutando ahora. await nextTick() espera a ese render, por ejemplo para leer el DOM nuevo.

Desenvolver. En la plantilla se desenvuelven las refs del nivel superior de <script setup>. Una ref dentro de un objeto normal ({ total: ref(0) }) no se desenvuelve en una expresión como {{ cart.total + 1 }}.

Options API. El código antiguo de Vue escribe el componente como un objeto con data(), methods y this.count. El código nuevo usa <script setup> y la Composition API.

Ejercicio

Elige la cantidad

El catálogo tiene tres tarjetas. En cada una añade una cantidad que empieza en 1, un botón «−» (nombre para el lector de pantalla: «Menos Pan blanco»), un botón «+» («Más Pan blanco») y una línea «2 × 1,20 € = 2,40 €» con la cantidad, el precio y el total. «−» está desactivado cuando la cantidad es 1. Cada tarjeta cuenta por su cuenta.

Vue

Test

Ponte a prueba

1. ¿Por qué let count = 0 y count++ en el componente no cambian la pantalla?
2. ¿Dónde escribes .value con const count = ref(0)?
3. count.value es 0. El manejador hace tres veces count.value++. ¿Qué pasa?
4. ¿Qué hace let bottles = count.value; bottles += 6;?
5. La página tiene dos ProductCard, cada una con su propio count. La clienta pulsa «Añadir» en la primera. ¿Qué pasa?

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