Счётчик должен увеличиваться каждую секунду, но застревает на 1. Найти и исправить причину.
function Timer() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(count + 1); // застревает на 1 }, 1000); return () => clearInterval(id); }, []); // эффект создаётся один раз return <p>{count}</p>;}
Подсказка
Эффект с [] выполняется один раз. Какое значение count «видит» колбэк интервала?
Проблемы
Что не так
Stale closure (устаревшее замыкание). Эффект с пустым массивом зависимостей создаётся один раз при монтировании. Колбэк setInterval замыкает count из первого рендера, где count === 0. Каждую секунду вызывается setCount(0 + 1) → всегда 1. React ставит 1 снова и снова, значение не растёт.
Решение
Решение 1 — функциональный апдейт (предпочтительно)
function Timer() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { // prev всегда актуален — не зависит от замкнутого count setCount((prev) => prev + 1); }, 1000); return () => clearInterval(id); }, []); return <p>{count}</p>;}
Решение 2 — пересоздавать эффект (работает, но хуже)
useEffect(() => { const id = setInterval(() => setCount(count + 1), 1000); return () => clearInterval(id);}, [count]); // новый интервал на каждое изменение count
Работает, но каждую секунду пересоздаёт интервал (clear + set) — лишняя работа и потенциальный дрейф. Функциональный апдейт чище.
Ключевая идея
Когда новое состояние зависит от предыдущего, используйте функциональную форму setState(prev => ...). Она не зависит от значения, замкнутого в момент создания колбэка, и решает проблему stale closure без пересоздания эффекта.