Решение ошибки 'window is not defined' в Next.js React
Пройдите тест, узнайте какой профессии подходите
Быстрый ответ
// 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).
Как обойти ошибку с window в SSR
В огромном космосе Next.js код functionионирует как на сервере, так и на клиенте. window
, как следует из названия, находится в доминионе клиента. На сервере его, понятное дело, нет, отсюда и возникает ошибка "Window is not defined".
Динамический импорт для модулей, специфичных для клиента
В работе с модулями, предназначенными исключительно для клиента, используйте динамический импорт — так они не вызовут ошибку на этапе серверного рендеринга.
import dynamic from 'next/dynamic';
const ClientSideComponent = dynamic(
() => import('./ClientOnlyComponent'), // импортируем как промис
{ ssr: false } // отключаем серверный рендеринг
);
Собственные хуки для контроля размеров окна
Хотите знать размеры окна браузера? Создадим пользовательский хук для этого!
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
.
class SafeWindowComponent extends React.Component {
componentDidMount() {
console.log(window.innerHeight); //Здесь window находится под защитой
}
render() {
return <div>Классовый компонент: работа с window осуществляется под надёжной защитой</div>;
}
}
Функциональные компоненты и useEffect
В функциональных компонентах useEffect
заменяет componentDidMount
, componentDidUpdate
и componentWillUnmount
, объединяя их функции.
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
.
const MyComponent = () => {
const isBrowser = typeof window !== 'undefined';
if (isBrowser) {
// Используем window
}
return (
// Ваш JSX
);
};
Визуализация
Воспринимайте window
как свет, который присутствует только на улице.
if (typeof window !== 'undefined') {
// На улице светло!
} else {
// Мы в помещении
}
Next.js можно представить как здание с разными комнатами: серверной и клиентской частями.
Здание Next.js:
Серверная часть: `window` нет
Клиентская часть: `window` в наличии!
Убедитесь, что вы находитесь в подходящей комнате, прежде чем пользоваться window
.
Сад кода Next.js
Как в саду, ваш код в Next.js требует грамотной "посадки" и заботы.
Правильно "сажаем" код, специфичный для клиента
Код для клиента следует располагать сообразно, чтобы он был функционален и его можно было легко поддерживать.
Регулярно очищаем память
Прислушиваетесь ли вы к событиям resize
или scroll
? После демонтирования компонента снимайте обработчики, чтобы освободить память для новых компонентов.
Условный рендеринг — ваш инструмент
Используйте условный рендеринг, чтобы компоненты соответствовали конкретному окружению выполнения и защищали от ошибок рендеринга на стороне клиента.