Сокращение лишних вызовов useEffect при подписке на события окна в React

Причина повторной подписки — новая inline-функция callback при каждом рендере App. Стабилизируйте её через useCallback и не пересоздавайте объект listenerOptions без изменения значений.

Короткий ответ

Что ответить на собеседовании

Причина повторной подписки — новая 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 не равен лишней производственной подписке.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее