Как безопасно использовать window/document в React?
Безопасное использование глобальных объектов window и document в библиотеке React требует понимания архитектурных особенностей серверного рендеринга, поскольку эти объекты принадлежа исключительно среде браузера. Когда код приложения исполняется на сервере в Node.js-окружении, глобальные объекты window и document просто не существуют, поэтому любые прямые попытки обратиться к ним приводят к критической ошибке выполнения и падению всего приложения. Проблема усугубляется тем, что современные фреймворки часто выполняют первичную отрисовку компонентов на бэкенде для ускорения загрузки и SEO-оптимизации, из-за чего код должен быть адаптирован под оба окружения.
Для предотвращения подобных ошибок в первую очередь следует перенести логику взаимодействия с браузерным API внутрь хука useEffect, который гарантированно исполняется исключительно на клиентской стороне после того, как компонент смонтирован в реальный DOM. Если же доступ к объекту window требуется получить во время инициализации или рендеринга компонента, необходимо использовать явную проверку окружения с помощью конструкции типа typeof window !== 'undefined', что позволяет безопасно выполнять код только в браузере.
Помимо базовых проверок, работа с такими ресурсами, как размеры окна браузера, локальное хранилище или файлы cookie, требует соблюдения определенных правил безопасности. Например, при отслеживании изменения размеров экрана через слушатели событий resize необходимо всегда возвращать функцию очистки из хука useEffect, чтобы предотвратить утечки памяти. При работе с localStorage важно учитывать, что на сервере данные недоступны, поэтому начальное состояние стейта должно иметь безопасное значение по умолчанию, а само чтение данных должно происходить после гидратации.
В современных версиях фреймворков, таких как Next.js 13 и выше, эту проблему во многом решают с помощью концепции серверных и клиентских компонентов. По умолчанию все компоненты являются серверными, что защищает разработчика от случайного использования браузерного API на бэкенде. Если же компонент требует интеграции с window или document, достаточно пометить его директивой клиентского рендеринга, что четко разграничит зоны ответственности между сервером и клиентом и сделает кодовую базу более предсказуемой и легко поддерживаемой.