Preservar y restablecer el estado
El estado está aislado entre componentes. React realiza un seguimiento de qué estado pertenece a qué componente según su ubicación en el árbol de la interfaz de usuario. Puedes controlar cuándo preservar el estado y cuándo restablecerlo entre re-renderizados.
Aprenderás
- Cuándo React decide preservar o restablecer el estado
- Cómo forzar a React a restablecer el estado de un componente
- Cómo las claves y los tipos afectan si el estado se preserva
El estado está vinculado a una posición en el árbol de renderizado
React construyeárboles de renderizadopara la estructura de componentes en tu interfaz de usuario.
Cuando le das estado a un componente, podrías pensar que el estado "vive" dentro del componente. Pero el estado en realidad lo mantiene React. React asocia cada pieza de estado que mantiene con el componente correcto según dónde se encuentre ese componente en el árbol de renderizado.
Aquí, solo hay una etiqueta JSX<Counter />, pero se renderiza en dos posiciones diferentes:
Así es como se ven como un árbol:


Árbol de React
Estos son dos contadores separados porque cada uno se renderiza en su propia posición en el árbol.Normalmente no tienes que pensar en estas posiciones para usar React, pero puede ser útil entender cómo funciona.
En React, cada componente en la pantalla tiene un estado completamente aislado. Por ejemplo, si renderizas dos componentesCounteruno al lado del otro, cada uno obtendrá sus propios estadosscore y hover, independientes.
Prueba a hacer clic en ambos contadores y observa que no se afectan entre sí:
Como puedes ver, cuando se actualiza un contador, solo se actualiza el estado de ese componente:


Actualizando el estado
React mantendrá el estado mientras renderices el mismo componente en la misma posición del árbol. Para ver esto, incrementa ambos contadores, luego elimina el segundo componente desmarcando la casilla de verificación “Render the second counter”, y luego agrégalo de nuevo marcándola otra vez:
Observa cómo en el momento en que dejas de renderizar el segundo contador, su estado desaparece por completo. Esto se debe a que cuando React elimina un componente, destruye su estado.


Eliminar un componente
Cuando marcas “Render the second counter”, un segundoCountery su estado se inicializan desde cero (score = 0) y se añaden al DOM.


Añadir un componente
React preserva el estado de un componente mientras se renderiza en su posición en el árbol de la interfaz de usuario.Si se elimina, o si un componente diferente se renderiza en la misma posición, React descarta su estado.
El mismo componente en la misma posición preserva el estado
En este ejemplo, hay dos etiquetas<Counter />diferentes:
Cuando marcas o desmarcas la casilla, el estado del contador no se reinicia. Ya sea queisFancyseatrue o false, siempre tienes un<Counter />como primer hijo deldivdevuelto por el componente raízApp:


Actualizar el estado deAppno reinicia elCounterporqueCounterpermanece en la misma posición
Es el mismo componente en la misma posición, así que, desde la perspectiva de React, es el mismo contador.
Pitfall
Recuerda que¡lo que importa para React es la posición en el árbol de la interfaz de usuario, no en el marcado JSX!Este componente tiene dos cláusulasreturncon diferentes etiquetas JSX<Counter />dentro y fuera delif:
Podrías esperar que el estado se reinicie cuando marcas la casilla, ¡pero no lo hace! Esto se debe a queambas etiquetas<Counter />se renderizan en la misma posición.React no sabe dónde colocas las condiciones en tu función. Todo lo que "ve" es el árbol que devuelves.
En ambos casos, el componenteAppdevuelve un<div>con<Counter />como primer hijo. Para React, estos dos contadores tienen la misma "dirección": el primer hijo del primer hijo de la raíz. Así es como React los empareja entre el renderizado anterior y el siguiente, independientemente de cómo estructures tu lógica.
Componentes diferentes en la misma posición reinician el estado
En este ejemplo, marcar la casilla reemplazará<Counter>con un<p>:
Aquí, cambias entrediferentestipos de componentes en la misma posición. Inicialmente, el primer hijo del<div>contenía unCounter. Pero cuando lo cambiaste por unp, React eliminó elCounterdel árbol de la interfaz de usuario y destruyó su estado.


CuandoCountercambia ap, elCounterse elimina y se añade elp.


Al cambiar de vuelta, elpse elimina y se añade elCounter.
Además,cuando renderizas un componente diferente en la misma posición, se reinicia el estado de todo su subárbol.Para ver cómo funciona esto, incrementa el contador y luego marca la casilla:
El estado del contador se reinicia cuando haces clic en la casilla. Aunque renderizas unCounter, el primer hijo deldivcambia de unsectiona undiv. Cuando el hijosectionfue eliminado del DOM, todo el árbol debajo de él (incluyendo elCountery su estado) también fue destruido.


Cuandosectioncambia adiv, elsectionse elimina y se añade el nuevodiv.


Al cambiar de vuelta, eldivse elimina y se añade el nuevosection
Como regla general,si quieres preservar el estado entre re-renderizados, la estructura de tu árbol necesita "coincidir"de un renderizado a otro. Si la estructura es diferente, el estado se destruye porque React destruye el estado cuando elimina un componente del árbol.
Pitfall
Por eso no debes anidar definiciones de funciones de componentes.
Aquí, la función del componenteMyTextField se define dentroMyComponent:
Cada vez que haces clic en el botón, ¡el estado del input desaparece! Esto se debe a que se crea una funcióndiferenteMyTextFieldpara cada renderizado deMyComponent. Estás renderizando un componentediferenteen la misma posición, por lo que React restablece todo el estado por debajo. Esto conduce a errores y problemas de rendimiento. Para evitar este problema,declara siempre las funciones de componentes en el nivel superior y no anides sus definiciones.
Restablecer el estado en la misma posición
Por defecto, React preserva el estado de un componente mientras permanece en la misma posición. Por lo general, esto es exactamente lo que quieres, por lo que tiene sentido como comportamiento predeterminado. Pero a veces, puedes querer restablecer el estado de un componente. Considera esta aplicación que permite a dos jugadores llevar un registro de sus puntuaciones durante cada turno:
Actualmente, cuando cambias de jugador, la puntuación se preserva. Los dosCounters aparecen en la misma posición, por lo que React los ve comoel mismoCountercuyo proppersonha cambiado.
Pero conceptualmente, en esta aplicación deberían ser dos contadores separados. Pueden aparecer en el mismo lugar en la interfaz de usuario, pero uno es un contador para Taylor y otro es un contador para Sarah.
Hay dos formas de restablecer el estado al cambiar entre ellos:
- Renderizar componentes en diferentes posiciones
- Dar a cada componente una identidad explícita con
key
Opción 1: Renderizar un componente en diferentes posiciones
Si quieres que estos dosCounters sean independientes, puedes renderizarlos en dos posiciones diferentes:
- Inicialmente,
isPlayerAestrue. Así que la primera posición contiene el estado deCounter, y la segunda está vacía. - Cuando haces clic en el botón "Siguiente jugador", la primera posición se vacía pero la segunda ahora contiene un
Counter.


Estado inicial


Haciendo clic en “siguiente”


Haciendo clic en “siguiente” de nuevo
El estado de cadaCounterse destruye cada vez que se elimina del DOM. Por eso se reinician cada vez que haces clic en el botón.
Esta solución es conveniente cuando solo tienes unos pocos componentes independientes renderizados en el mismo lugar. En este ejemplo, solo tienes dos, así que no es un problema renderizar ambos por separado en el JSX.
Opción 2: Reiniciar el estado con una clave
También hay otra forma, más genérica, de reiniciar el estado de un componente.
Es posible que hayas visto laskeys alrenderizar listas.¡Las claves no son solo para listas! Puedes usar claves para que React distinga entre cualquier componente. Por defecto, React usa el orden dentro del padre ("primer contador", "segundo contador") para discernir entre componentes. Pero las claves te permiten decirle a React que esto no es solo unprimercontador, o unsegundocontador, sino un contador específico, por ejemplo, el contador deTaylor. De esta manera, React sabrá el contador deTaylordondequiera que aparezca en el árbol.
En este ejemplo, los dos<Counter />no comparten estado aunque aparezcan en el mismo lugar en el JSX:
Cambiar entre Taylor y Sarah no preserva el estado. Esto se debe a queles diste diferenteskeys:
Especificar unakeyle dice a React que use lakeymisma como parte de la posición, en lugar de su orden dentro del padre. Por eso, aunque los renderices en el mismo lugar en el JSX, React los ve como dos contadores diferentes, y por lo tanto nunca compartirán estado. Cada vez que un contador aparece en pantalla, su estado se crea. Cada vez que se elimina, su estado se destruye. Alternar entre ellos reinicia su estado una y otra vez.
Nota
Recuerda que las claves no son globalmente únicas. Solo especifican la posicióndentro del padre.
Reiniciar un formulario con una clave
Reiniciar el estado con una clave es particularmente útil cuando se trabaja con formularios.
En esta aplicación de chat, el componente<Chat>contiene el estado de entrada de texto:
Intenta escribir algo en el campo de entrada y luego presiona “Alice” o “Bob” para elegir un destinatario diferente. Notarás que el estado de la entrada se conserva porque el<Chat>se renderiza en la misma posición en el árbol.
¡En muchas aplicaciones, este puede ser el comportamiento deseado, pero no en una aplicación de chat!No quieres permitir que el usuario envíe un mensaje que ya escribió a la persona equivocada debido a un clic accidental. Para solucionarlo, agrega unakey:
Esto asegura que cuando seleccionas un destinatario diferente, el componenteChatse recreará desde cero, incluyendo cualquier estado en el árbol debajo de él. React también recreará los elementos DOM en lugar de reutilizarlos.
Ahora cambiar el destinatario siempre borra el campo de texto:
Recapitulación
- React conserva el estado mientras el mismo componente se renderiza en la misma posición.
- El estado no se guarda en las etiquetas JSX. Está asociado con la posición en el árbol donde colocas ese JSX.
- Puedes forzar a un subárbol a restablecer su estado dándole una key diferente.
- No anides definiciones de componentes, o restablecerás el estado accidentalmente.
Try out some challenges
Challenge 1 of 5:Fix disappearing input text #
This example shows a message when you press the button. However, pressing the button also accidentally resets the input. Why does this happen? Fix it so that pressing the button does not reset the input text.
