Окно не определено в приложении Next.js React

В моем приложении Next.js я не могу получить доступ к window:

Unhandled Rejection (ReferenceError): window is not defined

componentWillMount() {
    console.info('window.innerHeight', window.innerHeight);
}

Окно не определено в приложении Next.js React

Переместите код в componentDidMount(), который выполняется только на клиенте, где доступен with window. Кроме того, componentWillMount() является устарел в v17https://github.com/zeit/next.js/wiki/FAQ#i-use-a-library-‌​what-throws-window-‌​is-undefined

Alexander Staroselsky 13.03.2019 21:59
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
123
1
195 680
16
Перейти к ответу Данный вопрос помечен как решенный

Ответы 16

Ответ принят как подходящий

Переместите код с componentWillMount() на componentDidMount():

componentDidMount() {
  console.info('window.innerHeight', window.innerHeight);
}

В Next.js componentDidMount() выполняется только на клиенте, где будут доступны window и другие специфичные для браузера API. Из Next.js вики:

Next.js is universal, which means it executes code first server-side, then client-side. The window object is only present client-side, so if you absolutely need to have access to it in some React component, you should put that code in componentDidMount. This lifecycle method will only be executed on the client. You may also want to check if there isn't some alternative universal library which may suit your needs.

В том же духе componentWillMount() будет устарел в v17 React, поэтому его фактически будет потенциально небезопасно использовать в самом ближайшем будущем.

componentWillMount() Хук жизненного цикла работает как на стороне сервера, так и на стороне клиента. В вашем случае сервер не будет знать о window или document во время обслуживания страницы, предлагается переместить код либо в

Решение 1:

componentDidMount()

Или, решение 2

Если это то, что вы хотите выполнять только в этом случае, вы можете написать что-то вроде:

componentWillMount() {
    if (typeof window !== 'undefined') {
        console.info('window.innerHeight', window.innerHeight);
    }
}

Другое решение является использование ̶p̶r̶o̶c̶e̶s̶s̶.̶b̶r̶o̶w̶s̶e̶r ̶ в выполнении лишь ̶ вашей команды в течение рендер на КЛИЕНТЕ ее стороны.

Но объект process устарел в Webpack5, а также в NextJS, потому что это переменная NodeJS только для серверной части.

Таким образом, мы должны использовать обратный объект window из браузера.

if (typeof window !== "undefined") {
  // Client-side-only code
}

Другим решением является использование хука реакции для замены componentDidMount:

useEffect(() => {
    // Client-side-only code
})

этот подход теперь уже используется устарелgithub.com/zeit/next.js/issues/5354#issuecomment-520305040

Anton Dozortsev 05.05.2020 23:09

Вы имеете в виду typeof window !== "undefined", потому что иначе это серверный запуск

Allwe 24.08.2020 17:29

серьезно, этому парню нужно отредактировать свой комментарий

Robbie Cook 17.01.2021 05:06

@Robbie - я попросил другого «парня» отредактировать комментарий.

Guy 06.10.2021 22:57

Лучший ответ, проверьте этот.

Hossein 23.11.2021 12:30

Я разрабатывал веб-сайт в реакции весь день. Первый проект тоже, и я застрял на этом весь день. Спасибо

Cardano Goat 02.02.2022 12:49

Без ССР

https://nextjs.org/docs/advanced-features/dynamic-import#with-no-ssr

import dynamic from 'next/dynamic'

const DynamicComponentWithNoSSR = dynamic(
  () => import('../components/hello3'),
  { ssr: false }
)

function Home() {
  return (
    <div>
      <Header />
      <DynamicComponentWithNoSSR />
      <p>HOME PAGE is here!</p>
    </div>
  )
}

export default Home

Это работает, даже если библиотека обращается к «окну» во время импорта.

Pedro Andrade 09.12.2019 23:02

@PeterMortensen, SSR = рендеринг на стороне сервера

Kate 09.07.2020 07:21

как получить доступ к переменной из внешнего js? как var myVar = "bla"

bl4ckck 20.01.2021 12:55

@ bl4ckck, к сожалению, вы можете использовать динамический импорт только для компонента (а не для функций)

Mr Washington 14.02.2021 01:55
TypeError: Cannot call a class as a function
Shahriar 07.11.2021 20:13

В конструкторе вашего класса Компонент вы можете добавить

if (typeof window === 'undefined') {
    global.window = {}
}

Пример:

import React, { Component } from 'react'

class MyClassName extends Component {

    constructor(props){
        super(props)
        ...
        if (typeof window === 'undefined') {
            global.window = {}
        }
}

Это позволит избежать ошибки (в моем случае ошибка произойдет после того, как я нажму кнопку перезагрузки страницы).

есть ли документация по global?

Jayen 01.10.2020 11:13

Я настоятельно рекомендую против этого подхода. Настройка global.window на стороне сервера не будет вести себя так же, как фактический объект window, предоставленный в среде браузера.

juliomalves 08.01.2022 20:44

Если вы используете Реагировать на хуки, вы можете переместить код в Хук Эффекта:

import * as React from "react";

export const MyComp = () => {

  React.useEffect(() => {
    // window is accessible here.
    console.info("window.innerHeight", window.innerHeight);
  }, []);

  return (<div></div>)
}

Код внутри useEffect выполняется только на клиенте (в браузере), поэтому он имеет доступ к window.

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

Mahmoud Mousa 02.01.2020 13:02

Это работает, хотя, если вы делаете что-то внутри useEffect, которое обновляет состояние, обязательно добавьте пустой [] в качестве зависимостей.

deowk 26.02.2020 08:47

Я столкнулся с той же проблемой, когда разрабатывал веб-приложение в next.js. Это устранило мою проблему, вам нужно обратиться к объекту окна в методе жизненного цикла или к хуку реакции. Например, скажем, я хочу создать переменную хранилища с избыточностью, и в этом хранилище я хочу использовать объект Windows, я могу сделать это следующим образом:

let store
useEffect(()=>{
    store = createStore(rootReducers,   window.__REDUX_DEVTOOLS_EXTENSION__ && 
    window.__REDUX_DEVTOOLS_EXTENSION__())
 }, [])
 ....

Итак, в основном, когда вы работаете с объектом окна, всегда используйте хук для игры или метод жизненного цикла componentDidMount()

Мне нужно получить доступ к хешу из URL-адреса, поэтому я придумал это

const hash = global.window && window.location.hash;

самое аккуратное решение

sidonaldson 09.06.2021 16:33

это решение тоже работает, и вы можете просто определить его внутри useEffect()

Ismoil Shokirov 10.01.2022 14:51

Для таких случаев в Next.js есть Динамический импорт.

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

Ошибка возникает из-за того, что окно еще недоступно, а компонент все еще монтируется. Вы можете получить доступ к объекту окна после того, как компонент смонтирован.

Вы можете создать очень полезный хук для получения динамики window.innerHeight или window.innerWidth.

const useDeviceSize = () => {

  const [width, setWidth] = useState(0)
  const [height, setHeight] = useState(0)

  const handleWindowResize = () => {
    setWidth(window.innerWidth);
    setHeight(window.innerHeight);
  }

  useEffect(() => {
    // component is mounted and window is available
    handleWindowResize();
    window.addEventListener('resize', handleWindowResize);
    // unsubscribe from the event on component unmount
    return () => window.removeEventListener('resize', handleWindowResize);
  }, []);

  return [width, height]

}

export default useDeviceSize 

Вариант использования:

const [width, height] = useDeviceSize();

Умная. Очень хорошо.

Bill Haack 30.12.2021 20:35

Это будет работать каждый раз, когда вы изменяете размер окна, это полезно в случаях, когда вы работаете с css -> чтобы ваши страницы реагировали

Ismoil Shokirov 10.01.2022 14:53

Дата: 08.06.2021

Проверьте, существует ли объект окна или нет, а затем следуйте коду вместе с ним.

 function getSelectedAddress() {
    if (typeof window === 'undefined') return;

    // Some other logic
 }

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

const [height, setHeight] = useState();

useEffect(() => {
    if (!height) setHeight(window.innerHeight - 140);
    window.addEventListener("resize", () => {
        setHeight(window.innerHeight - 140);
    });
}, []);

Вот простой в использовании обходной путь, который я сделал.

const runOnClient = (func: () => any) => {
  if (typeof window !== "undefined") {
    if (window.document.readyState == "loading") {
      window.addEventListener("load", func);
    } else {
      func();
    }
  }
};

Применение:

runOnClient(() => {
// access window as you like
})

// or async
runOnClient(async () => {
// remember to catch errors that might be raised in promises, and use the `await` keyword wherever needed
})

Это лучше, чем просто typeof window !== "undefined", потому что если вы просто проверите, что окно не является неопределенным, оно не будет работать, если ваша страница была перенаправлена ​​на, оно просто срабатывает один раз при загрузке. Но этот обходной путь работает, даже если страница была перенаправлена, а не только один раз во время загрузки.

Это будет переопределять window.onload каждый раз

OwnageIsMagic 18.10.2021 13:41

@OwnageIsMagic нет, если вы видите, он устанавливает window.onload, только если страница не загружена.

roj1512 18.10.2021 18:18

рассмотрите этот фрагмент runOnClient(f1); runOnClient(f2)

OwnageIsMagic 18.10.2021 18:25

также читайте об этом stackoverflow.com/questions/2414750/…

OwnageIsMagic 18.10.2021 18:30

@OwnageIsMagic Я добавил слушателя, надеюсь, теперь это исправлено.

roj1512 19.10.2021 12:52

Я хочу оставить этот подход, который показался мне интересным, для будущих исследователей. Он использует специальный хук useEventListener, который можно использовать во многих других случаях.

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

Таким образом, это закончится так:

import { useRef, useEffect } from 'react'

export const useEventListener = (eventName, handler, element) => {
  const savedHandler = useRef()

  useEffect(() => {
    savedHandler.current = handler
  }, [handler])

  useEffect(() => {
    element = !element ? window : element
    const isSupported = element && element.addEventListener
    if (!isSupported) return

    const eventListener = (event) => savedHandler.current(event)

    element.addEventListener(eventName, eventListener)

    return () => {
      element.removeEventListener(eventName, eventListener)
    }
  }, [eventName, element])
}

Лучшее решение

import dynamic from 'next/dynamic';

const Chart = dynamic(()=> import('react-apexcharts'), {
    ssr:false,
})

Ваш ответ может быть улучшен с помощью дополнительной вспомогательной информации. Пожалуйста, редактировать добавьте дополнительную информацию, например цитаты или документацию, чтобы другие могли подтвердить правильность вашего ответа. Дополнительную информацию о том, как писать хорошие ответы, можно найти в справочном центре.

Community 02.12.2021 17:06

Означает ли это, что эта страница будет отображаться в браузере, а не на сервере? для меня вся цель использования next js - это рендеринг на сервере

Shamseer Ahammed 25.01.2022 15:47

Если это приложение NextJS и внутри _document.js, используйте ниже:

<script dangerouslySetInnerHTML = {{
        __html: `
            var innerHeight = window.innerHeight;
        `
        }} />

Другие вопросы по теме