No, no es obligatorio incluir como dependencia de useEffect el estado que se actualiza dentro del efecto. Lo que sí es obligatorio es incluir como dependencia todo valor que se lee dentro del efecto y que puede cambiar entre renders.
📌 Diferencia clave
| Caso | ¿Debe estar en dependencias? | Motivo |
|---|---|---|
setState(valorFijo) | ❌ No | No depende de nada cambiante |
setState(prev => ...) | ❌ No | React maneja el valor internamente |
setState(valorDerivadoDeX) | ✅ Sí (x debe estar) | Porque x puede cambiar |
if (condiciónBasadaEnY) setState(...) | ✅ Sí (y debe estar) | Porque la lógica depende de y |
📌 Ejemplo correcto
useEffect(() => {
setCount(prev => prev + 1); // ✅ No necesita `count` como dependencia
}, []);
📌 Ejemplo incorrecto
useEffect(() => {
setCount(count + 1); // ❌ Si usás `count`, debe estar en dependencias
}, []);
Este segundo ejemplo puede usar un valor obsoleto de count si no lo incluís en el array de dependencias.
✅ Conclusión
No es obligatorio incluir el estado que se actualiza, salvo que lo estés leyendo dentro del efecto.
Si usás
prev => ..., estás protegido contra valores stale y no necesitás incluir el estado como dependencia.Si usás el valor directamente (
count + 1), sí tenés que incluirlo.
¿Querés que te arme una guía visual de dependencias en useEffect, con ejemplos de cuándo incluir y cuándo no incluir cada variable?
Excelente observación, Gabriel. Es cierto que los efectos (useEffect) se ejecutan después del render, pero eso no significa que siempre tengan acceso al estado más actualizado si ese estado fue capturado antes del render actual. Vamos a descomponerlo:
📌 ¿Por qué puede llegar un valor desactualizado al efecto?
1. Captura de valores previos
Si el efecto depende de una variable (count, por ejemplo), y esa variable fue capturada en el cierre (closure) de una función anterior, entonces el efecto puede estar usando una versión anterior del estado, aunque se ejecute después del render.
useEffect(() => {
const interval = setInterval(() => {
console.log(count); // ❌ puede estar desactualado
}, 1000);
return () => clearInterval(interval);
}, []);
Aunque el efecto se ejecuta después del render, el
countque se usa dentro delsetIntervales el que estaba en el cierre cuando el efecto se definió.Como el array de dependencias está vacío (
[]), el efecto no se vuelve a ejecutar cuandocountcambia.
2. Efectos con dependencias vacías
Cuando no incluís una variable en el array de dependencias, el efecto no se actualiza con los nuevos valores de esa variable. Esto es lo que genera el “valor stale” (obsoleto).
3. Solución con updater function
Si en vez de leer el valor directamente usás una función:
setCount(prev => prev + 1);
No necesitás depender de
counten el efecto.React garantiza que el
preves el valor más reciente, incluso si el efecto no se volvió a ejecutar.
✅ Conclusión
Aunque los efectos se ejecutan después del render, pueden usar valores desactualizados si esos valores fueron capturados antes del render actual y no están en las dependencias.
Por eso, el updater function (prev => ...) es tan valioso: evita depender de valores que podrían estar stale.
¿Querés que te arme un ejemplo con setInterval donde se ve claramente cómo el valor se queda obsoleto si no se usa prev => ..., y cómo se soluciona?
Perfecto, te muestro un ejemplo práctico con setInterval para que veas cómo un valor puede quedar obsoleto si no usás la versión con prev => ..., y cómo se soluciona:
❌ Ejemplo con valor stale
import { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
// Usa "count" directamente
setCount(count + 1);
console.log("Dentro del intervalo:", count);
}, 1000);
return () => clearInterval(interval);
}, []); // Dependencias vacías
return <div>{count}</div>;
}
Qué pasa aquí:
El efecto se ejecuta una sola vez (porque
[]está vacío).El
countcapturado en el cierre es siempre 0.Entonces, cada tick del intervalo hace
setCount(0 + 1)→ el estado nunca pasa de 1.El
console.logmuestra siempre el valor viejo.
✅ Ejemplo correcto con updater function
import { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
// Usa la función con "prev"
setCount(prev => prev + 1);
console.log("Dentro del intervalo:", count);
}, 1000);
return () => clearInterval(interval);
}, []); // Dependencias vacías
return <div>{count}</div>;
}
Qué pasa aquí:
El efecto se ejecuta una sola vez.
Cada tick del intervalo usa el valor más reciente de
countgracias aprev.El estado se incrementa correctamente: 0 → 1 → 2 → 3 → …
El componente se re-renderiza con el valor actualizado.
✅ Conclusión
El efecto se ejecuta al final del ciclo de render, pero si no incluís la variable en dependencias, el cierre captura un valor viejo.
La forma segura de evitarlo es usar la versión con updater function (prev => ...), que siempre trabaja con el estado más reciente.
¿Querés que te arme un diagrama visual que muestre cómo el cierre captura el valor viejo en el primer ejemplo y cómo el updater function evita ese problema?