Решение ошибки 'window is not defined' в Next.js React

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

jsx
Скопировать код
// useEffect приходит на помощь!
import { useEffect } from 'react';

const SafeWindow = () => {
  useEffect(() => {
    // Здесь безопасно обращаться к объекту window
    console.log(window.location.href);
  }, []); // Эффект будет вызван однократно после монтирования

  return <div>Надёжный компонент — Window-Safe</div>;
};

export default SafeWindow;

Комбинация window и useEffect — ваш щит от ошибок «window not defined» в Next.js в процессе серверной отрисовки (SSR).

Кинга Идем в IT: пошаговый план для смены профессии

Как обойти ошибку с window в SSR

В огромном космосе Next.js код functionионирует как на сервере, так и на клиенте. window, как следует из названия, находится в доминионе клиента. На сервере его, понятное дело, нет, отсюда и возникает ошибка "Window is not defined".

Динамический импорт для модулей, специфичных для клиента

В работе с модулями, предназначенными исключительно для клиента, используйте динамический импорт — так они не вызовут ошибку на этапе серверного рендеринга.

jsx
Скопировать код
import dynamic from 'next/dynamic';

const ClientSideComponent = dynamic(
  () => import('./ClientOnlyComponent'), // импортируем как промис
  { ssr: false } // отключаем серверный рендеринг
);

Собственные хуки для контроля размеров окна

Хотите знать размеры окна браузера? Создадим пользовательский хук для этого!

jsx
Скопировать код
function useWindowSize() {
  const isClient = typeof window === 'object';

  function getSize() {
    return isClient ? { width: window.innerWidth, height: window.innerHeight } : { width: undefined, height: undefined };
  }

  const [windowSize, setWindowSize] = useState(getSize);

  useEffect(() => {
    if (!isClient) {
      return false;
    }
    
    function handleResize() {
      setWindowSize(getSize());
    }

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return windowSize;
}

Жизненные циклы и хуки: они стоят на страже доступа к window

componentWillMount отошёл в тень. Теперь роль "шерифов" исполняют useEffect и componentDidMount, обеспечивая надёжный доступ к window.

componentDidMount в классовых компонентах

Классовые компоненты используют метод componentDidMount как надежный барьер для безопасной работы с window.

jsx
Скопировать код
class SafeWindowComponent extends React.Component {
  componentDidMount() {
    console.log(window.innerHeight); //Здесь window находится под защитой
  }

  render() {
    return <div>Классовый компонент: работа с window осуществляется под надёжной защитой</div>;
  }
}

Функциональные компоненты и useEffect

В функциональных компонентах useEffect заменяет componentDidMount, componentDidUpdate и componentWillUnmount, объединяя их функции.

jsx
Скопировать код
import { useState, useEffect } from 'react';

const SafeWindowFunctional = () => {
  const [innerHeight, setInnerHeight] = useState(0);

  useEffect(() => {
    setInnerHeight(window.innerHeight); //Здесь можно работать без страха
    return () => {
      // Завершаем работу
    };
  }, []);

  return <div>Высота внутреннего окна — {innerHeight}px</div>;
};

Универсальные библиотеки и переделка компонентов

Код должен корректно functionировать как на сервере, так и на клиенте. При работе с window вы можете воспользоваться универсальными библиотеками — они работают в обоих случаях.

Переписываем компоненты с учётом window и SSR

Приведите компоненты в соответствие с особыми требованиями серверного рендеринга, чтобы они безопасно обращались к window.

jsx
Скопировать код
const MyComponent = () => {
  const isBrowser = typeof window !== 'undefined';

  if (isBrowser) {
    // Используем window
  }

  return (
    // Ваш JSX
  );
};

Визуализация

Воспринимайте window как свет, который присутствует только на улице.

JS
Скопировать код
if (typeof window !== 'undefined') {
  // На улице светло!
} else {
  // Мы в помещении
}

Next.js можно представить как здание с разными комнатами: серверной и клиентской частями.

Markdown
Скопировать код
Здание Next.js:
Серверная часть: `window` нет
Клиентская часть: `window` в наличии!

Убедитесь, что вы находитесь в подходящей комнате, прежде чем пользоваться window.

Сад кода Next.js

Как в саду, ваш код в Next.js требует грамотной "посадки" и заботы.

Правильно "сажаем" код, специфичный для клиента

Код для клиента следует располагать сообразно, чтобы он был функционален и его можно было легко поддерживать.

Регулярно очищаем память

Прислушиваетесь ли вы к событиям resize или scroll? После демонтирования компонента снимайте обработчики, чтобы освободить память для новых компонентов.

Условный рендеринг — ваш инструмент

Используйте условный рендеринг, чтобы компоненты соответствовали конкретному окружению выполнения и защищали от ошибок рендеринга на стороне клиента.

Полезные материалы

  1. Маршрутизация: страницы и компоновка | Next.js
  2. Window – Веб API | MDN
  3. Использование хука Effect – React
  4. Оптимизация: Ленивая загрузка | Next.js
  5. Проблемы и решения · vercel/next.js · GitHub