v19.2Latest

Ciclo de vida de los Efectos Reactivos

Los Efectos tienen un ciclo de vida diferente al de los componentes. Los componentes pueden montarse, actualizarse o desmontarse. Un Efecto solo puede hacer dos cosas: comenzar a sincronizar algo y, más tarde, dejar de sincronizarlo. Este ciclo puede ocurrir varias veces si tu Efecto depende de props y estado que cambian con el tiempo. React proporciona una regla de linter para verificar que hayas especificado correctamente las dependencias de tu Efecto. Esto mantiene tu Efecto sincronizado con las props y el estado más recientes.

Aprenderás
  • Cómo el ciclo de vida de un Efecto es diferente al ciclo de vida de un componente
  • Cómo pensar en cada Efecto individual de forma aislada
  • Cuándo tu Efecto necesita volver a sincronizarse y por qué
  • Cómo se determinan las dependencias de tu Efecto
  • Qué significa que un valor sea reactivo
  • Qué significa un array de dependencias vacío
  • Cómo React verifica que tus dependencias sean correctas con un linter
  • Qué hacer cuando no estás de acuerdo con el linter

El ciclo de vida de un Efecto

Cada componente de React pasa por el mismo ciclo de vida:

  • Un componentese montacuando se añade a la pantalla.
  • Un componentese actualizacuando recibe nuevas props o estado, generalmente en respuesta a una interacción.
  • Un componentese desmontacuando se elimina de la pantalla.

Es una buena forma de pensar en los componentes, peronoen los Efectos.En su lugar, intenta pensar en cada Efecto de forma independiente del ciclo de vida de tu componente. Un Efecto describe cómosincronizar un sistema externocon las props y el estado actuales. A medida que tu código cambia, la sincronización necesitará ocurrir con más o menos frecuencia.

Para ilustrar este punto, considera este Efecto que conecta tu componente a un servidor de chat:

El cuerpo de tu Efecto especifica cómocomenzar la sincronización:

La función de limpieza devuelta por tu Efecto especifica cómodetener la sincronización:

Intuitivamente, podrías pensar que Reactcomenzaría la sincronizacióncuando tu componente se monta ydetendría la sincronizacióncuando tu componente se desmonta. Sin embargo, ¡esta no es toda la historia! A veces, también puede ser necesariocomenzar y detener la sincronización varias vecesmientras el componente permanece montado.

Veamospor quéesto es necesario,cuándoocurre ycómopuedes controlar este comportamiento.

Nota

Algunos Efectos no devuelven ninguna función de limpieza.Más a menudo de lo que piensas,querrás devolver una, pero si no lo haces, React se comportará como si hubieras devuelto una función de limpieza vacía.

Por qué la sincronización puede necesitar ocurrir más de una vez

Imagina que este componenteChatRoomrecibe una proproomIdque el usuario selecciona en un menú desplegable. Supongamos que inicialmente el usuario selecciona la sala"general"comoroomId. Tu aplicación muestra la sala de chat"general":

Después de que se muestre la interfaz de usuario, React ejecutará tu Efecto paracomenzar la sincronización.Se conecta a la sala"general":

Hasta ahora, todo bien.

Más tarde, el usuario selecciona una sala diferente en el menú desplegable (por ejemplo,"travel"). Primero, React actualizará la interfaz de usuario:

Piensa en lo que debería suceder a continuación. El usuario ve que"travel"es la sala de chat seleccionada en la interfaz de usuario. Sin embargo, el Efecto que se ejecutó la última vez todavía está conectado a la sala"general". La proproomIdha cambiado, por lo que lo que tu Efecto hizo en ese momento (conectarse a la sala"general") ya no coincide con la interfaz de usuario.

En este punto, quieres que React haga dos cosas:

  1. Dejar de sincronizar con el antiguoroomId(desconectarse de la sala"general")
  2. Comenzar a sincronizar con el nuevoroomId(conectarse a la sala"travel")

Afortunadamente, ¡ya le has enseñado a React cómo hacer ambas cosas!El cuerpo de tu Efecto especifica cómo comenzar a sincronizar, y tu función de limpieza especifica cómo dejar de sincronizar. Todo lo que React necesita hacer ahora es llamarlas en el orden correcto y con las props y el estado correctos. Veamos exactamente cómo sucede eso.

Cómo React resincroniza tu Efecto

Recuerda que tu componenteChatRoomha recibido un nuevo valor para su proproomId. Antes era"general", y ahora es"travel". React necesita resincronizar tu Efecto para volver a conectarte a una sala diferente.

Paradejar de sincronizar,React llamará a la función de limpieza que tu Efecto devolvió después de conectarse a la sala"general". Dado queroomIdera"general", la función de limpieza se desconecta de la sala"general":

Luego, React ejecutará el Efecto que has proporcionado durante este renderizado. Esta vez,roomIdes"travel", por lo quecomenzará a sincronizarcon la sala de chat"travel"(hasta que su función de limpieza también sea llamada eventualmente):

Gracias a esto, ahora estás conectado a la misma sala que el usuario eligió en la interfaz de usuario. ¡Desastre evitado!

Cada vez que tu componente se vuelva a renderizar con unroomIddiferente, tu Efecto se resincronizará. Por ejemplo, supongamos que el usuario cambiaroomIdde"travel" a "music". React volverá adejar de sincronizartu Efecto llamando a su función de limpieza (desconectándote de la sala"travel"). Luegocomenzará a sincronizarnuevamente ejecutando su cuerpo con la nueva proproomId(conectándote a la sala"music").

Finalmente, cuando el usuario va a una pantalla diferente,ChatRoomse desmonta. Ahora ya no hay necesidad de permanecer conectado en absoluto. Reactdejará de sincronizartu Efecto por última vez y te desconectará de la sala de chat"music".

Pensando desde la perspectiva del Efecto

Recapitulemos todo lo que ha sucedido desde la perspectiva del componenteChatRoom:

  1. ChatRoomse montó conroomIdestablecido en"general"
  2. ChatRoomse actualizó conroomIdestablecido en"travel"
  3. ChatRoomse actualizó conroomIdestablecido en"music"
  4. ChatRoomse desmontó

Durante cada uno de estos puntos en el ciclo de vida del componente, tu Efecto hizo cosas diferentes:

  1. Tu Efecto se conectó a la sala"general"
  2. Tu Efecto se desconectó de la sala"general"y se conectó a la sala"travel"
  3. Tu Efecto se desconectó de la sala"travel"y se conectó a la sala"music"
  4. Tu Efecto se desconectó de la sala"music"

Ahora pensemos en lo que sucedió desde la perspectiva del Efecto mismo:

La estructura de este código podría inspirarte a ver lo sucedido como una secuencia de períodos de tiempo que no se superponen:

  1. Tu Efecto se conectó a la sala"general"(hasta que se desconectó)
  2. Tu Efecto se conectó a la sala"travel"(hasta que se desconectó)
  3. Tu Efecto se conectó a la sala"music"(hasta que se desconectó)

Anteriormente, estabas pensando desde la perspectiva del componente. Cuando mirabas desde la perspectiva del componente, era tentador pensar en los Efectos como "devoluciones de llamada" o "eventos del ciclo de vida" que se activan en un momento específico, como "después de un renderizado" o "antes de desmontar". Esta forma de pensar se complica muy rápido, por lo que es mejor evitarla.

En su lugar, concéntrate siempre en un único ciclo de inicio/detención a la vez. No debería importar si un componente se está montando, actualizando o desmontando. Todo lo que necesitas hacer es describir cómo iniciar la sincronización y cómo detenerla. Si lo haces bien, tu Efecto será resistente a iniciarse y detenerse tantas veces como sea necesario.

Esto podría recordarte cómo no piensas si un componente se está montando o actualizando cuando escribes la lógica de renderizado que crea JSX. Describes lo que debería estar en la pantalla, y Reactse encarga del resto.

Cómo React verifica que tu Efecto puede resincronizarse

Aquí hay un ejemplo en vivo con el que puedes interactuar. Presiona "Abrir chat" para montar el componenteChatRoom:

Observa que cuando el componente se monta por primera vez, ves tres registros:

  1. ✅ Conectando a la sala "general" en https://localhost:1234...(solo desarrollo)
  2. ❌ Desconectado de la sala "general" en https://localhost:1234.(solo desarrollo)
  3. ✅ Conectando a la sala "general" en https://localhost:1234...

Los dos primeros registros son solo para desarrollo. En desarrollo, React siempre vuelve a montar cada componente una vez.

React verifica que tu Efecto pueda resincronizarse forzándolo a hacerlo inmediatamente en desarrollo.Esto podría recordarte abrir una puerta y cerrarla una vez extra para comprobar si la cerradura funciona. React inicia y detiene tu Efecto una vez extra en desarrollo para verificarque has implementado bien su limpieza.

La razón principal por la que tu Efecto se resincronizará en la práctica es si algún dato que utiliza ha cambiado. En el sandbox de arriba, cambia la sala de chat seleccionada. Observa cómo, cuando elroomIdcambia, tu Efecto se resincroniza.

Sin embargo, también hay casos más inusuales en los que la resincronización es necesaria. Por ejemplo, intenta editar laserverUrlen el sandbox de arriba mientras el chat está abierto. Observa cómo el Efecto se resincroniza en respuesta a tus ediciones del código. En el futuro, React podría añadir más características que dependan de la resincronización.

Cómo sabe React que necesita resincronizar el Efecto

Quizás te preguntes cómo supo React que tu Efecto necesitaba resincronizarse después de que cambiara elroomId. Es porquetú le dijiste a Reactque su código depende deroomIdal incluirlo en lalista de dependencias:

Así es como funciona:

  1. Sabías queroomIdes una prop, lo que significa que puede cambiar con el tiempo.
  2. Sabías que tu Efecto leeroomId(por lo que su lógica depende de un valor que puede cambiar más tarde).
  3. Por eso lo especificaste como dependencia de tu Efecto (para que se resincronice cuandoroomIdcambie).

Cada vez que tu componente se vuelva a renderizar, React mirará el array de dependencias que has pasado. Si alguno de los valores en el array es diferente del valor en la misma posición que pasaste durante el renderizado anterior, React resincronizará tu Efecto.

Por ejemplo, si pasaste["general"]durante el renderizado inicial, y luego pasaste["travel"]durante el siguiente renderizado, React comparará"general" y "travel". Estos son valores diferentes (comparados conObject.is), por lo que React resincronizará tu Efecto. Por otro lado, si tu componente se vuelve a renderizar pero elroomIdno ha cambiado, tu Efecto permanecerá conectado a la misma sala.

Cada Efecto representa un proceso de sincronización separado

Resiste la tentación de añadir lógica no relacionada a tu Efecto solo porque esta lógica necesita ejecutarse al mismo tiempo que un Efecto que ya escribiste. Por ejemplo, digamos que quieres enviar un evento de análisis cuando el usuario visita la sala. Ya tienes un Efecto que depende deroomId, por lo que podrías sentirte tentado a añadir la llamada de análisis allí:

Pero imagina que más tarde añades otra dependencia a este Efecto que necesita restablecer la conexión. Si este Efecto se resincroniza, también llamará alogVisit(roomId)para la misma sala, lo cual no era tu intención. Registrar la visitaes un proceso separadode conectar. Escríbelos como dos Efectos separados:

Cada Efecto en tu código debe representar un proceso de sincronización separado e independiente.

En el ejemplo anterior, eliminar un Efecto no rompería la lógica del otro Efecto. Esta es una buena indicación de que sincronizan cosas diferentes, y por lo tanto tenía sentido separarlos. Por otro lado, si divides una pieza de lógica cohesiva en Efectos separados, el código puede parecer "más limpio" pero serámás difícil de mantener.Por eso debes pensar si los procesos son iguales o separados, no si el código se ve más limpio.

Los Efectos "reaccionan" a valores reactivos

Tu Efecto lee dos variables (serverUrl y roomId), pero solo especificasteroomIdcomo dependencia:

¿Por quéserverUrlno necesita ser una dependencia?

Esto se debe a queserverUrlnunca cambia debido a una nueva renderización. Siempre es el mismo sin importar cuántas veces se vuelva a renderizar el componente y por qué. Dado queserverUrlnunca cambia, no tendría sentido especificarlo como una dependencia. ¡Después de todo, las dependencias solo hacen algo cuando cambian con el tiempo!

Por otro lado,roomIdpuede ser diferente en una nueva renderización.Las props, el estado y otros valores declarados dentro del componente sonreactivosporque se calculan durante la renderización y participan en el flujo de datos de React.

SiserverUrlfuera una variable de estado, sería reactiva. Los valores reactivos deben incluirse en las dependencias:

Al incluirserverUrlcomo dependencia, te aseguras de que el Efecto se vuelva a sincronizar después de que cambie.

Intenta cambiar la sala de chat seleccionada o editar la URL del servidor en este sandbox:

Cada vez que cambias un valor reactivo comoroomId o serverUrl, el Efecto se vuelve a conectar al servidor de chat.

Qué significa un Efecto con dependencias vacías

¿Qué sucede si mueves tantoserverUrlcomoroomIdfuera del componente?

Ahora el código de tu Efecto no usaningúnvalor reactivo, por lo que sus dependencias pueden estar vacías ([]).

Pensando desde la perspectiva del componente, el array de dependencias vacío[]significa que este Efecto se conecta a la sala de chat solo cuando el componente se monta, y se desconecta solo cuando el componente se desmonta. (Recuerda que React aúnlo volvería a sincronizar una vez extraen desarrollo para probar tu lógica a fondo).

Sin embargo, sipiensas desde la perspectiva del Efecto,no necesitas pensar en el montaje y desmontaje en absoluto. Lo importante es que has especificado lo que tu Efecto hace para comenzar y dejar de sincronizar. Hoy, no tiene dependencias reactivas. Pero si alguna vez quieres que el usuario cambieroomId o serverUrlcon el tiempo (y se volverían reactivos), el código de tu Efecto no cambiará. Solo necesitarás agregarlos a las dependencias.

Todas las variables declaradas en el cuerpo del componente son reactivas

Las props y el estado no son los únicos valores reactivos. Los valores que calculas a partir de ellos también son reactivos. Si las props o el estado cambian, tu componente se volverá a renderizar, y los valores calculados a partir de ellos también cambiarán. Por eso todas las variables del cuerpo del componente utilizadas por el Efecto deben estar en la lista de dependencias del Efecto.

Supongamos que el usuario puede elegir un servidor de chat en el menú desplegable, pero también puede configurar un servidor predeterminado en los ajustes. Supón que ya has colocado el estado de los ajustes en uncontexto, por lo que lees lossettingsdesde ese contexto. Ahora calculas laserverUrlen función del servidor seleccionado de las props y el servidor predeterminado:

En este ejemplo,serverUrlno es una prop ni una variable de estado. Es una variable regular que calculas durante el renderizado. Pero como se calcula durante el renderizado, puede cambiar debido a una nueva renderización. Por eso es reactiva.

Todos los valores dentro del componente (incluyendo props, estado y variables en el cuerpo de tu componente) son reactivos. Cualquier valor reactivo puede cambiar en una nueva renderización, por lo que necesitas incluir los valores reactivos como dependencias del Efecto.

En otras palabras, los Efectos "reaccionan" a todos los valores del cuerpo del componente.

Deep Dive
¿Pueden los valores globales o mutables ser dependencias?

React verifica que hayas especificado cada valor reactivo como dependencia

Si tu linter estáconfigurado para React,verificará que cada valor reactivo utilizado por el código de tu Efecto esté declarado como su dependencia. Por ejemplo, este es un error de lint porque tantoroomIdcomoserverUrlson reactivos:

Esto puede parecer un error de React, pero en realidad React está señalando un error en tu código. TantoroomIdcomoserverUrlpueden cambiar con el tiempo, pero te estás olvidando de resincronizar tu Efecto cuando cambian. Seguirás conectado alroomId y serverUrliniciales incluso después de que el usuario elija valores diferentes en la interfaz.

Para corregir el error, sigue la sugerencia del linter de especificarroomId y serverUrlcomo dependencias de tu Efecto:

Prueba esta corrección en el sandbox de arriba. Verifica que el error del linter haya desaparecido y que el chat se reconecte cuando sea necesario.

Nota

En algunos casos, Reactsabeque un valor nunca cambia aunque esté declarado dentro del componente. Por ejemplo, lafunción setdevuelta poruseStatey el objeto ref devuelto poruseRefsonestables—se garantiza que no cambian en una nueva renderización. Los valores estables no son reactivos, por lo que puedes omitirlos de la lista. Incluirlos está permitido: no cambiarán, así que no importa.

Qué hacer cuando no quieres resincronizar

En el ejemplo anterior, has corregido el error del linter listandoroomId y serverUrlcomo dependencias.

Sin embargo, podrías en su lugar "demostrar" al linter que estos valores no son valores reactivos,es decir, queno puedencambiar como resultado de una nueva renderización. Por ejemplo, siserverUrl y roomIdno dependen del renderizado y siempre tienen los mismos valores, puedes moverlos fuera del componente. Ahora no necesitan ser dependencias:

También puedes moverlosdentro del Efecto.No se calculan durante el renderizado, por lo que no son reactivos:

Los Efectos son bloques de código reactivos.Se resincronizan cuando cambian los valores que lees dentro de ellos. A diferencia de los controladores de eventos, que solo se ejecutan una vez por interacción, los Efectos se ejecutan siempre que sea necesaria la sincronización.

No puedes “elegir” tus dependencias.Tus dependencias deben incluir cadavalor reactivoque leas en el Efecto. El linter lo exige. A veces esto puede llevar a problemas como bucles infinitos y a que tu Efecto se resincronice con demasiada frecuencia. ¡No soluciones estos problemas suprimiendo el linter! Esto es lo que puedes intentar en su lugar:

  • Verifica que tu Efecto represente un proceso de sincronización independiente.Si tu Efecto no sincroniza nada,podría ser innecesario.Si sincroniza varias cosas independientes,divídelo.
  • Si quieres leer el último valor de las props o el estado sin “reaccionar” a él y resincronizar el Efecto,puedes dividir tu Efecto en una parte reactiva (que mantendrás en el Efecto) y una parte no reactiva (que extraerás en algo llamadoEvento de Efecto).Lee sobre separar Eventos de Efectos.
  • Evita depender de objetos y funciones como dependencias.Si creas objetos y funciones durante el renderizado y luego los lees desde un Efecto, serán diferentes en cada renderizado. Esto hará que tu Efecto se resincronice cada vez.Lee más sobre cómo eliminar dependencias innecesarias de los Efectos.
Pitfall

El linter es tu amigo, pero sus poderes son limitados. El linter solo sabe cuándo las dependencias estánmal. No sabela mejormanera de resolver cada caso. Si el linter sugiere una dependencia, pero agregarla causa un bucle, no significa que se deba ignorar el linter. Necesitas cambiar el código dentro (o fuera) del Efecto para que ese valor no sea reactivo y nonecesiteser una dependencia.

Si tienes una base de código existente, es posible que tengas algunos Efectos que suprimen el linter así:

En lassiguientespáginas, aprenderás cómo arreglar este código sin romper las reglas. ¡Siempre vale la pena arreglarlo!

Recapitulación

  • Los componentes pueden montarse, actualizarse y desmontarse.
  • Cada Efecto tiene un ciclo de vida separado del componente que lo rodea.
  • Cada Efecto describe un proceso de sincronización separado que puedeiniciar y detener.
  • Cuando escribes y lees Efectos, piensa desde la perspectiva de cada Efecto individual (cómo iniciar y detener la sincronización) en lugar de desde la perspectiva del componente (cómo se monta, actualiza o desmonta).
  • Los valores declarados dentro del cuerpo del componente son “reactivos”.
  • Los valores reactivos deben resincronizar el Efecto porque pueden cambiar con el tiempo.
  • El linter verifica que todos los valores reactivos utilizados dentro del Efecto se especifiquen como dependencias.
  • Todos los errores marcados por el linter son legítimos. Siempre hay una manera de arreglar el código para no romper las reglas.

Try out some challenges

Challenge 1 of 5:Fix reconnecting on every keystroke #

In this example, the ChatRoom component connects to the chat room when the component mounts, disconnects when it unmounts, and reconnects when you select a different chat room. This behavior is correct, so you need to keep it working.

However, there is a problem. Whenever you type into the message box input at the bottom, ChatRoom also reconnects to the chat. (You can notice this by clearing the console and typing into the input.) Fix the issue so that this doesn’t happen.


Ciclo de Vida de los Efectos Reactivos | React Learn - Reflow Hub