React 19 de Cero a Experto / Estado
Inmutabilidad: cómo React ve tus cambios
El estado real de una app son OBJETOS y ARRAYS —una lista de gastos, un formulario con muchos campos—. Actualizarlos tiene una regla de oro que ya dominas del supercurso de JavaScript: la INMUTABILIDAD. En React no es solo buena práctica: es cómo React DETECTA que algo cambió. Mutar el estado hace que React "no vea" el cambio y no repinte. Hoy cierras el círculo entre la inmutabilidad del SC-02 y el motor de React.
La dirección de la casa, otra vez
El bug: mutar no repinta
Mira el error en vivo. El botón "mal" hace push sobre el array del estado —y la UI no cambia,
aunque el dato sí:
El botón "mal" SÍ agrega al array (el dato cambia), pero como es el MISMO array (misma
referencia), React compara la dirección, la ve idéntica, y concluye "nada que repintar". El
botón "bien" crea un array NUEVO con spread ([...gastos, "Nuevo"]), cuya referencia es distinta
—y React repinta—. Es exactamente la inmutabilidad del SC-02, ahora con una razón de peso: React
la NECESITA para funcionar.
Los patrones inmutables (los mismos del SC-02)
Estado complejo: objetos en el estado
Un caso muy común —un objeto de formulario en el estado— se actualiza campo por campo con spread:
const [form, setForm] = useState({ nombre: "", valor: 0, categoria: "comida" });
// actualizar UN campo sin perder los demás: spread + el campo nuevo
function cambiarNombre(nuevo: string) {
setForm({ ...form, nombre: nuevo }); // copia todo, pisa 'nombre'
}
// un manejador genérico para cualquier campo:
function cambiarCampo(campo: string, valor: string) {
setForm((prev) => ({ ...prev, [campo]: valor })); // clave dinámica (SC-02)
}{ ...form, nombre: nuevo } copia todos los campos del formulario y pisa solo nombre —el
resto sobrevive—. Sin el spread, setForm({ nombre: nuevo }) BORRARÍA valor y categoria. La
clave dinámica [campo]: valor (del SC-02) te deja un manejador único para todos los campos.
Para estado MUY anidado, este spread se vuelve verboso —ahí entran herramientas como Immer o el
useReducer de la próxima lección—, pero el principio es siempre el mismo: copia, no mutes.
Tienes const [items, setItems] = useState([...]). Haces items.push(nuevo) y luego setItems(items). La lista en pantalla no se actualiza. ¿Por qué?
Mini-reto
Actualiza estado complejo sin mutar: 1) un estado con un array de tareas { id, texto, hecha }; 2) un botón que agregue una tarea con [...prev, nueva]; 3) botones para marcar una
como hecha (map que copia y cambia solo esa con { ...t, hecha: true }) y para borrarla
(filter). Usa la forma funcional prev => en todos. Comprueba que si en algún punto mutas
(push, asignación directa), la UI se congela.
Qué sigue
Sabes manejar estado dentro de un componente. Pero a menudo DOS componentes necesitan compartir el mismo estado —un input y una lista, un filtro y unos resultados—. La próxima lección enseña el patrón "lifting state up": subir el estado al ancestro común para que ambos lo compartan. La clave de coordinar componentes.