Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама
Senior Хуки

Що таке «застаріле замикання» в хуках React і як його виправити?

Кожен рендер компонента - окремий виклик функції зі своїми значеннями props і стану. Функції, створені під час рендеру (обробники, колбеки ефектів), «запам'ятовують» значення цього рендеру. Якщо функція живе довше за рендер, вона бачить старі значення - це застаріле замикання (stale closure).

Класичний приклад - інтервал:

function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);   // count завжди 0 - замикання першого рендеру
    }, 1000);
    return () => clearInterval(id);
  }, []);   // ефект створено один раз

  return <p>{count}</p>;   // зупиняється на 1
}

Способи виправити:

1. Функціональне оновлення - не читати стан у замиканні взагалі:

setCount((c) => c + 1);

Найкращий варіант, коли нове значення залежить лише від попереднього.

2. Додати значення в залежності - ефект перезапуститься з новим замиканням:

useEffect(() => { /* ... */ }, [count]);

Коректно, але для інтервалу означає перестворення таймера щосекунди.

3. useEffectEvent - логіка, що має бачити свіжі значення, але не перезапускати ефект:

const onTick = useEffectEvent(() => setCount(count + step));
useEffect(() => {
  const id = setInterval(onTick, 1000);
  return () => clearInterval(id);
}, []);

4. useRef з поточним значенням - старий спосіб (ref оновлюється на кожному рендері, колбек читає ref.current). Працює, але useEffectEvent робить це явніше.

Де ще трапляється:

  • обробники сторонніх бібліотек, зареєстровані один раз (карта, WebSocket);
  • useCallback з неповними залежностями - мемоізована функція бачить старий стан;
  • асинхронні функції: після await значення змінних - ті, що були на момент виклику, навіть якщо стан уже змінився.

Профілактика: правило лінтера react-hooks/exhaustive-deps знаходить більшість застарілих замикань у ефектах і мемоізації. Попередження «missing dependency» - майже завжди справжня помилка, а не надмірна прискіпливість лінтера.

Докладніше в документації: Видалення залежностей ефекту

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання