No necesitás un efecto
¿Cuántos de mis useEffect no deberían existir?
Al terminar vas a poder explicar
- Reconocer los cuatro patrones de efecto innecesario más comunes
- Derivar valores durante el render en vez de duplicarlos en estado
- Distinguir 'sincronizar con algo externo' de 'reaccionar a una interacción'
Si tuviera que elegir una sola lección de todo este curso para que alguien se lleve, sería esta. La mayoría de los useEffect que hay escritos en el mundo no deberían existir.
Un efecto sirve para una cosa: sincronizar React con un sistema externo. Una suscripción, un temporizador, una conexión, una API del navegador, un widget de terceros. Si no hay ningún sistema externo del otro lado, casi seguro no necesitás un efecto.
Estos son los cuatro casos que aparecen una y otra vez.
1. Transformar datos para mostrarlos
1// ✗ Un render de más, y una fuente de verdad de más.2const [items, setItems] = useState([]);3const [visibles, setVisibles] = useState([]);4 5useEffect(() => {6 setVisibles(items.filter(i => !i.archivado));7}, [items]);8 9// ✓ Se calcula durante el render. Imposible que quede viejo.10const visibles = items.filter(i => !i.archivado);Fijate el costo real de la primera versión: React renderiza con visibles vacío, commitea, el navegador pinta, y recién ahí corre el efecto que dispara otro render. Hay un frame donde el usuario ve la lista vacía.
2. Reaccionar a una interacción del usuario
1// ✗ ¿Por qué se mandó el evento? Hay que leer todo el componente para saberlo.2useEffect(() => {3 if (producto) registrarVisita(producto.id);4}, [producto]);5 6// ✓ La causa está donde ocurrió.7function alElegirProducto(p) {8 setProducto(p);9 registrarVisita(p.id);10}La pregunta que separa los dos casos es: ¿esto pasó porque el componente se mostró, o porque el usuario hizo algo? Si fue el usuario, va en el manejador. Ahí tenés el evento, tenés el contexto, y quien lea el código en seis meses va a entender por qué se disparó.
3. Reiniciar el estado cuando cambia una prop
1// ✗ Renderiza una vez con el estado del usuario ANTERIOR y después se corrige.2useEffect(() => {3 setComentario("");4}, [usuarioId]);5 6// ✓ Una key distinta es otro componente: React lo remonta con estado limpio.7<Perfil key={usuarioId} usuarioId={usuarioId} />Esto lo entendiste en la lección de reconciliación: cambiar la key hace que React no encuentre el fiber y monte uno nuevo. Todo el estado de adentro se reinicia solo, sin un efecto y sin el frame intermedio con datos del usuario anterior.
4. Ajustar estado cuando cambian las props
Cuando sólo querés reiniciar parte del estado, ni siquiera hace falta un efecto: se puede ajustar durante el render.
1function Lista({ items }) {2 const [seleccion, setSeleccion] = useState(null);3 4 // Patrón oficial: comparar contra estado previo DURANTE el render.5 const [itemsPrevios, setItemsPrevios] = useState(items);6 if (items !== itemsPrevios) {7 setItemsPrevios(items);8 setSeleccion(null);9 }10 // …11}Parece que rompe una regla, pero no: React detecta el setState durante el render, descarta la salida y vuelve a renderizar el mismo componente antes de tocar nada. Nunca se pinta el estado intermedio. Es más rápido que un efecto y no produce parpadeo.
Lo que pasa cuando un efecto no tiene freno
AGENDADOpaso 1 / 1200Sin hooks en este paso.
Árbol de fibers
- <HostRoot>
Salida
Nada en el host todavía. React no commiteó ningún cambio.
createRoot().render()
Montaje inicial. No hay árbol previo, así que todo se va a crear desde cero.
La corrida se detuvo: Bucle infinito: el render disparó otro render más de 50 veces seguidas. Casi siempre es un setState dentro del cuerpo del componente o un useEffect sin array de dependencias.
VER EL CÓDIGO QUE SE ESTÁ TRAZANDO
1function BucleInfinito() {2 const [n, setN] = useState(0);3 4 useEffect(() => {5 setN(v => v + 1); // ⚠️ sin array de deps6 }); // ⚠️ corre después de CADA render7 8 return <span>n = {n}</span>;9}Con el foco acá: ← → un paso · espacio reproduce · Inicio/Fin van a los extremos.
Cuándo un efecto SÍ es la respuesta
Queda una lista corta y clara:
- Conectarte a algo: WebSocket, EventSource, un canal de mensajes.
- Suscribirte a una API del navegador:
resize,online, un observer. - Controlar un widget que no es de React: un mapa, un editor, un reproductor.
- Disparar temporizadores e intervalos.
- Traer datos, si no tenés un framework que lo haga por vos.
Todos tienen algo en común: hay un sistema del otro lado que no sabe nada de React, y alguien tiene que sincronizarlo. Ese alguien es el efecto.
Antes de seguir · predecí
Un formulario tiene que limpiarse cuando el usuario termina de enviarlo con éxito. ¿Efecto o no?
Lo que te llevás
Si podés calcularlo durante el render, calculalo. Si pasa por una interacción, va en el manejador. Si hay que reiniciar todo al cambiar de dato, usá una key. Los efectos son para sincronizar con el mundo de afuera — y ese mundo es mucho más chico de lo que parece.
Antes de marcarla, comprobá
- ¿Podés explicar reconocer los cuatro patrones de efecto innecesario más comunes?
- ¿Podés explicar derivar valores durante el render en vez de duplicarlos en estado?
- ¿Podés explicar distinguir 'sincronizar con algo externo' de 'reaccionar a una interacción'?