Actualizar objetos en el estado
El estado puede contener cualquier tipo de valor de JavaScript, incluidos objetos. Pero no debes cambiar directamente los objetos que almacenas en el estado de React. En su lugar, cuando quieras actualizar un objeto, necesitas crear uno nuevo (o hacer una copia de uno existente) y luego establecer el estado para usar esa copia.
Aprenderás
- Cómo actualizar correctamente un objeto en el estado de React
- Cómo actualizar un objeto anidado sin mutarlo
- Qué es la inmutabilidad y cómo no romperla
- Cómo hacer que la copia de objetos sea menos repetitiva con Immer
¿Qué es una mutación?
Puedes almacenar cualquier tipo de valor de JavaScript en el estado.
Hasta ahora has trabajado con números, cadenas y booleanos. Este tipo de valores de JavaScript son "inmutables", es decir, no se pueden cambiar o son "de solo lectura". Puedes desencadenar un nuevo renderizado parareemplazarun valor:
El estadoxcambió de0 a 5, pero elnúmero0en síno cambió. No es posible realizar cambios en los valores primitivos incorporados como números, cadenas y booleanos en JavaScript.
Ahora considera un objeto en el estado:
Técnicamente, es posible cambiar el contenido deel objeto en sí.Esto se llama una mutación:
Sin embargo, aunque los objetos en el estado de React son técnicamente mutables, debes tratarloscomo sifueran inmutables, como los números, booleanos y cadenas. En lugar de mutarlos, siempre debes reemplazarlos.
Tratar el estado como de solo lectura
En otras palabras, debestratar cualquier objeto de JavaScript que coloques en el estado como de solo lectura.
Este ejemplo mantiene un objeto en el estado para representar la posición actual del puntero. Se supone que el punto rojo se mueve cuando tocas o mueves el cursor sobre el área de vista previa. Pero el punto permanece en la posición inicial:
El problema está en este fragmento de código.
Este código modifica el objeto asignado apositiondesdeel renderizado anterior.Pero sin usar la función de establecimiento de estado, React no tiene idea de que el objeto ha cambiado. Por lo tanto, React no hace nada en respuesta. Es como intentar cambiar el pedido después de que ya te has comido la comida. Aunque mutar el estado puede funcionar en algunos casos, no lo recomendamos. Debes tratar el valor de estado al que tienes acceso en un renderizado como de solo lectura.
Para realmentedesencadenar un nuevo renderizadoen este caso,crea unnuevoobjeto y pásalo a la función de establecimiento de estado:
ConsetPosition, le estás diciendo a React:
- Reemplaza
positioncon este nuevo objeto - Y renderiza este componente nuevamente
Observa cómo el punto rojo ahora sigue tu puntero cuando tocas o pasas el cursor sobre el área de vista previa:
Copiar objetos con la sintaxis de propagación
En el ejemplo anterior, el objetopositionsiempre se crea nuevo a partir de la posición actual del cursor. Pero a menudo, querrás incluir datosexistentescomo parte del nuevo objeto que estás creando. Por ejemplo, es posible que quieras actualizarsolo uncampo en un formulario, pero mantener los valores anteriores para todos los demás campos.
Estos campos de entrada no funcionan porque los manejadoresonChangemutan el estado:
Por ejemplo, esta línea muta el estado de un renderizado anterior:
La forma confiable de obtener el comportamiento que buscas es crear un nuevo objeto y pasarlo asetPerson. Pero aquí, también quierescopiar los datos existentes en élporque solo uno de los campos ha cambiado:
Puedes usar la sintaxis de propagación...de objetospara que no necesites copiar cada propiedad por separado.
¡Ahora el formulario funciona!
Observa que no declaraste una variable de estado separada para cada campo de entrada. Para formularios grandes, mantener todos los datos agrupados en un objeto es muy conveniente, ¡siempre y cuando lo actualices correctamente!
Ten en cuenta que la sintaxis de propagación...es "superficial": solo copia las cosas un nivel de profundidad. Esto la hace rápida, pero también significa que si quieres actualizar una propiedad anidada, tendrás que usarla más de una vez.
Actualizar un objeto anidado
Considera una estructura de objeto anidado como esta:
Si quisieras actualizarperson.artwork.city, está claro cómo hacerlo con mutación:
Pero en React, ¡tratas el estado como inmutable! Para cambiarcity, primero necesitarías producir el nuevo objetoartwork(precargado con datos del anterior), y luego producir el nuevo objetopersonque apunta al nuevoartwork:
O, escrito como una única llamada de función:
Esto se vuelve un poco verboso, pero funciona bien para muchos casos:
Escribe lógica de actualización concisa con Immer
Si tu estado está profundamente anidado, podrías consideraraplanarlo.Pero, si no quieres cambiar la estructura de tu estado, podrías preferir un atajo para los spreads anidados.Immeres una biblioteca popular que te permite escribir usando la sintaxis conveniente pero mutante y se encarga de producir las copias por ti. Con Immer, el código que escribes parece que estás "rompiendo las reglas" y mutando un objeto:
Pero a diferencia de una mutación regular, ¡no sobrescribe el estado anterior!
Para probar Immer:
- Ejecuta
npm install use-immerpara agregar Immer como dependencia - Luego reemplaza
import { useState } from 'react'conimport { useImmer } from 'use-immer'
Aquí está el ejemplo anterior convertido a Immer:
Observa cuánto más concisos se han vuelto los controladores de eventos. Puedes mezclar y combinaruseState y useImmeren un solo componente tanto como quieras. Immer es una excelente manera de mantener los controladores de actualización concisos, especialmente si hay anidamiento en tu estado y copiar objetos conduce a código repetitivo.
Resumen
- Trata todo el estado en React como inmutable.
- Cuando almacenas objetos en el estado, mutarlos no activará renderizados y cambiará el estado en las "instantáneas" de renderizados anteriores.
- En lugar de mutar un objeto, crea unanuevaversión del mismo, y activa un nuevo renderizado estableciendo el estado a esa versión.
- Puedes usar la sintaxis de propagación de objetos
{...obj, something: 'newValue'}para crear copias de objetos. - La sintaxis de propagación es superficial: solo copia un nivel de profundidad.
- Para actualizar un objeto anidado, necesitas crear copias en todos los niveles desde el lugar que estás actualizando.
- Para reducir el código repetitivo de copia, usa Immer.
Try out some challenges
Challenge 1 of 3:Fix incorrect state updates #
This form has a few bugs. Click the button that increases the score a few times. Notice that it does not increase. Then edit the first name, and notice that the score has suddenly “caught up” with your changes. Finally, edit the last name, and notice that the score has disappeared completely.
Your task is to fix all of these bugs. As you fix them, explain why each of them happens.
