Retirar el Referencia de Valor Más Reciente: Eventos de Efecto con useEffectEvent
Aprenda cómo useEffectEvent en React 19.2 separa la lógica no reactiva de un efecto, elimina los trucos con el ref latest-value y determina cuándo este hook no es la herramienta adecuada.
Muchos conjuntos de código de React contienen un useRef cuya única función es almacenar “el valor más reciente” para que un efecto no tenga que depender de él. Este truco funciona, pero oculta la intención real. React 19.2 hace que useEffectEvent sea estable, brindando a ese patrón un hook de primera clase. Al final, podrás separar la lógica reactiva y no reactiva dentro de un efecto, y saber cuándo el hook no es aplicable.
La matriz de dependencias que nunca gana
El conflicto surge con conexiones de larga duración: salas de chat, feeds de WebSocket, sesiones de análisis, suscripciones. Un ChatRoom recibe roomId y theme. El efecto debe reconectarse cuando roomId cambia, pero solo lee theme para aplicar un estilo a un mensaje después de conectarse.
Agregue theme al array de dependencias y al cambiar al modo oscuro se interrumpe el socket sin motivo alguno. Si no lo incluye, la regla de linting de los hooks emite una advertencia; si la silencia, la función de callback mantiene capturado el theme de una renderización anterior, lo que constituye un cierre obsoleto clásico.
function ChatRoom({ roomId, theme }) {
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => {
showNotification('Connected!', theme); // stale after theme changes
});
connection.connect();
return () => connection.disconnect();
}, [roomId, theme]); // theme shouldn't trigger a reconnect
}
La solución temporal que adoptaron la mayoría de los equipos
La solución habitual consistía en copiar theme a una referencia en cada renderización y leer ref.current dentro de la función de callback. Funciona, pero es fácil olvidar el paso de sincronización, y nada en el código indica que la lógica sea deliberadamente no reactiva.
Separar la lógica de eventos con useEffectEvent
useEffectEvent extrae la parte no reactiva de un efecto en una función separada. Esa función siempre accede a los últimos valores de props y estado, su llamada nunca vuelve a ejecutar el efecto, y no se incluye en el array de dependencias.
A continuación, la notificación pasa a onConnected. El efecto solo crea la conexión, registra el manejador, se conecta y se desconecta al finalizar.
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme); // always fresh, never a dependency
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => onConnected());
connection.connect();
return () => connection.disconnect();
}, [roomId]); // only roomId matters now
}
Solo roomId queda como dependencia. theme no se ha ocultado del analizador; simplemente se ha reclasificado. Mostrar un mensaje emergente cuando se establece una conexión es una reacción a un evento, por lo que se trata de lógica de eventos y no de sincronización. Cambiar el tema ya no vuelve a establecer la conexión, y el siguiente mensaje emergente sigue utilizando el tema actual.
Conceptualmente, esto es lo que hacía manualmente el truco con los referentes: React mantiene la función a la que se hace referencia desde el efecto apuntando a la cierre del último renderizado, y ahora la intención es clara.
Beneficios
- Sin cierres obsoletos. El efecto de eventos lee los valores más recientes sin necesidad de actualizar manualmente los referentes.
La documentación de React ha instado a los desarrolladores a separar los eventos de los efectos desde la era de React 18; 19.2 finalmente respalda ese consejo con un hook estable. Para conocer más sobre esta versión, consulte nuestro resumen de Activity, useEffectEvent y renderizado estático en React 19.2.
Antes de usarlo
useEffectEvent no reemplaza a useEffect, ni es una forma general de reducir las matrices de dependencias.
- Úselo solo cuando un cambio en el valor no debe reiniciar el efecto. Si sí lo hace, ese valor debe estar en el array.
- Nunca llame a un Effect Event durante la renderización; invoquelo desde dentro de los efectos, como en las devoluciones de llamada de suscripción o temporizadores.
- Manténgalo local a su componente en lugar de pasarlo a los hijos como prop.
- Si la mayor parte de un efecto termina dentro de un Effect Event, es probable que la lógica pertenezca a un manejador de eventos normal.
Antes de la versión 19.2, el hook existía solo en versiones experimentales, por lo que los proyectos con 19.0 o 19.1 deben actualizarse para utilizar la API estable.
Puntos clave
- Cada solución temporal tipo “mantener el valor más reciente en un ref” es candidata para
useEffectEvent. - Para cada dependencia, pregúntese si un cambio debe reiniciar el efecto; si no, el código que lo lee corresponde a lógica de eventos.
Lecturas relacionadas
- Elegir la herramienta correcta de React: Derive, Handle, Fetch, Defer o Effect — Una guía de decisiones para reemplazar las llamadas reflexivas a useEffect por valores derivados, controladores de eventos, una capa de datos, useTransition, useMemo medido y las API de React 19.
- Dejar de sincronizar el estado con useEffect: Un patrón más seguro en React — Aprenda por qué usar useEffect para sincronizar el estado derivado causa condiciones de carrera y renders adicionales, y cómo reemplazarlo con la derivación en tiempo de renderizado y el atributo key.
- Construir un estado-toy useState para entender snapshots, colas y efectos — Aprende cómo se comportan los hooks de React al crear un pequeño useState, y luego utiliza ese modelo para explicar las colas de actualización, los snapshots obsoletos, las cierres, la limpieza de efectos, los refs y el agrupamiento.
- Formularios asincrónicos en React 19 con use, useActionState y useOptimistic — Cómo use(), useActionState, useFormStatus y useOptimistic reemplazan las banderas manuales de carga y error en React 19, junto con los problemas que cada hook oculta silenciosamente.
- Retirar dependencias heredadas de JavaScript: CRA, Moment, jQuery y más — Qué bibliotecas y herramientas de JavaScript obsoletas eliminar gradualmente, por qué cada una se convirtió en un problema, qué las reemplaza y cómo evitar que su lista de dependencias se deteriore nuevamente.