Причина повторной подписки — новая inline-функция callback при каждом рендере App. Стабилизируйте её через useCallback и не пересоздавайте объект listenerOptions без изменения значений.
Сокращение лишних вызовов useEffect при подписке на события окна в React
Причина повторной подписки — новая inline-функция callback при каждом рендере App. Стабилизируйте её через useCallback и не пересоздавайте объект listenerOptions без изменения значений.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Необходимо избавиться от избыточных срабатываний useEffect в компоненте WindowEvent, чтобы обработчик события добавлялся и удалялся только при реальных изменениях зависимостей.
function WindowEvent({
eventName,
callback,
listenerOptions
}: {
eventName: string;
callback: EventListener;
listenerOptions?: EventListenerOptions;
}){
useEffect(() => {
console.log('useEffect');
window.addEventListener(eventName, callback, listenerOptions);
return () => {
window.removeEventListener(eventName, callback, listenerOptions);
};
}, [eventName, callback, listenerOptions]);
return null;
}
export default function App() {
const [visible, setVisible] = useState(false);
const toggleVisibility = useCallback(() => {
setVisible(prev => !prev);
}, []);
return (
<>
<Button onClick={toggleVisibility} />
<WindowEvent eventName="click" callback={() => console.log("click")} />
{visible && (<div>lorem ipsum</div>)}
</>
);
}
Ответ
Причина повторной подписки — новая inline-функция callback при каждом рендере App. Стабилизируйте её через useCallback и не пересоздавайте объект listenerOptions без изменения значений.
const onWindowClick = useCallback(() => console.log('click'), []);
// В JSX:
<WindowEvent eventName="click" callback={onWindowClick} />
toggleVisibility уже стабилен и не является источником проблемы. Если callback использует состояние, перечислите зависимости: нельзя просто поставить [] и получить устаревшее замыкание. Для options с capture можно зависеть от конкретного boolean, а не от всего объекта. Если добавляются passive/once/signal, их изменения тоже нужно учитывать. Правильная очистка в исходном эффекте уже есть. Повторный setup/cleanup в development Strict Mode не равен лишней производственной подписке.