Видалення Referencе «Latest-Value»: Ефекти подій за допомогою useEffectEvent
Дізнайтеся, як useEffectEvent у React 19.2 відокремлює нереактивну логіку від ефекту, усуває хаки з ref latest-value та визначає, коли цей хук є непідходящим інструментом.
Багато проєктів на React містять функцію useRef, єдиною роллю якої є зберігання „найновішої значення“, щоб ефект не мусив від неї залежати. Цей підхід працює, але приховує справжню мету. У React 19.2 функція useEffectEvent стала стабільною, що надало цьому паттерну статус першокласного хука. До кінця ви зможете розділяти реактивну та нереактивну логіку всередині ефекту та знати, коли цей хук не застосовується.
Масив залежностей, який не може перемогти
Конфлікт виникає при довготривалих з’єднаннях: чат-кімнатах, потоках WebSocket, сеансах аналітики, підписках. Об’єкт ChatRoom отримує параметри roomId та theme. Ефект має підключатися знову, коли змінюється roomId, але читає theme лише для форматування повідомлення після підключення.
Додайте theme до масиву залежностей, і перехід у темний режим без причини знищує сокет. Якщо його не додавати, правило перевірки хуків починає сигналізувати про проблему; якщо ж його ігнорувати, калебек продовжує зберігати значення theme з попереднього відображення, що є класичним прикладом застарілої замиканості.
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
}
Рішення, яке використовували більшість команд
Зазвичай для вирішення проблеми копіювали theme у ref під час кожного відображення та читали ref.current усередині калебека. Це працює, але крок синхронізації легко забувається, і в коді немає жодних позначок про те, що логіка навмисно є нереактивною.
Відокремлення логіки подій за допомогою useEffectEvent
useEffectEvent витягує нереактивну частину ефекту у окрему функцію. Ця функція завжди отримує найновіші значення пропсів та стану, її виклик ніколи не запускає ефект заново, і вона не потрапляє до масиву залежностей.
Нижче сповіщення переходить у стан onConnected. Цей ефект лише створює з’єднання, реєструє обробник подій, підключається та від’єднується під час очищення.
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
}
Як залежність залишається лише roomId. theme не було приховано від інструменту лінтингу; його просто перекласифікували. Показ тости під час встановлення з’єднання є реакцією на подію, тож це логіка обробки подій, а не синхронізація. Зміна теми більше не відновлює з’єднання, і наступний тости все одно використовує поточну тему.
Концептуально це саме те, що робив мануальний хак з ref: React зберігає функцію, яку ви викликаєте з ефекту, так, щоб вона посилалася на закриття останнього рендеру, і тепер намір стає зрозумілим.
Що ви отримуєте
- Більше немає застарілих закриттів. Ефект подій читає найновіші значення без необхідності вручну оновлювати ref.
У документації React з часів версії 18 рекомендувалося роз’єднувати події від ефектів; версія 19.2 нарешті підтримує цю пораду за допомогою стабільного хука. Щодо інших нововведень цієї версії, дивіться наш огляд функцій Activity, useEffectEvent та статичного відображення у React 19.2.
Перш ніж почати використовувати
useEffectEvent не замінює useEffect, і це не є універсальним способом скорочення масивів залежностей.
- Використовуйте його лише тоді, коли зміна значення не повинна перезапускати ефект. Якщо це має статися, значення потрібно розмістити в масиві.
- Ніколи не викликайте Effect Event під час відображення; ініціюйте його зсередини ефектів, наприклад, через функції-відповідачі або таймери.
- Зберігайте його локально у компоненті, замість того щоб передавати його дочірнім елементам як проп.
- Якщо більша частина ефекту знаходиться всередині Effect Event, логіка, ймовірно, має бути у звичайному обробнику подій.
До версії 19.2 цей хук існував лише у експериментальних версіях, тому проекти на версіях 19.0 або 19.1 повинні оновитися, щоб використовувати стабільний API.
Основні висновки
- Кожен спосіб «зберігання останнього значення у ref» є кандидатом на використання з
useEffectEvent. - Для кожної залежності запитайте себе, чи має зміна перезапускати ефект; якщо ні, код, який її читає, є логікою події.
Пов’язана література
- Вибір правильного інструменту для React: Derive, Handle, Fetch, Defer чи Effect — посібник з прийняття рішень щодо заміни рефлексивних викликів useEffect на похідні значення, обробники подій, шар даних, useTransition, measured useMemo та API React 19.
- Припиніть синхронізацію стану за допомогою useEffect: безпечніший підхід у React — дізнайтеся, чому використання useEffect для синхронізації похідного стану спричиняє ситуації змагання та зайве відображення елементів, та як замінити його на отримання даних під час відображення та використання атрибута key.
- Створіть іграшковий useState для розуміння сніппетів, черг та ефектів — Дізнайтеся, як працюють хуки React, створивши невеликий useState, а потім використайте цю модель для пояснення черг оновлень, застарілих сніппетів, закриттів функцій, очищення ефектів, refs та групування операцій.
- Асинхронні форми в React 19 з use, useActionState та useOptimistic — Як use(), useActionState, useFormStatus та useOptimistic замінюють ручне завантаження та позначки помилок у React 19, а також які перешкоди приховують кожен з цих хуків.
- Відмова від застарілих залежностей JavaScript: CRA, Moment, jQuery та інше — Які застарілі бібліотеки та інструменти JavaScript варто поступово відмовлятися, чому кожен з них став проблемою, що може їх замінити та як запобігти знову появі проблем у вашому списку залежностей.