Window 未在 Next.js React 应用程序中定义
Window is not defined in Next.js React app
在我的 Next.js 应用程序中,我似乎无法访问 window
:
Unhandled Rejection (ReferenceError): window is not defined
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
将代码从 componentWillMount()
移动到 componentDidMount()
:
componentDidMount() {
console.log('window.innerHeight', window.innerHeight);
}
在 Next.js 中,componentDidMount()
仅在 window
和其他浏览器特定 API 可用的客户端上执行。来自 Next.js wiki:
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()
将在 React v17 中成为 deprecated,因此在不久的将来使用它实际上可能不安全。
componentWillMount()
生命周期挂钩在服务器端和客户端都有效。在您的情况下,服务器在页面服务期间不知道 window
或 document
,建议将代码移至
解决方案一:
componentDidMount()
或者,解决方案 2
如果你只是想在其中表演,那么你可以这样写:
componentWillMount() {
if (typeof window !== 'undefined') {
console.log('window.innerHeight', window.innerHeight);
}
}
̶A̶n̶o̶t̶h̶e̶r̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶i̶s̶ ̶b̶y̶ ̶u̶s̶i̶n̶g̶ ̶p̶r̶o̶c̶e̶s̶s̶.̶b̶r̶o̶w̶s̶e̶r
̶ ̶t̶o̶ ̶j̶u̶s̶t̶ ̶e̶x̶e̶c̶u̶t̶e̶ ̶ ̶y̶o̶u̶r̶ ̶c̶o̶m̶m̶a̶n̶d̶ ̶d̶u̶r̶i̶n̶g̶ ̶r̶e̶n̶d̶e̶r̶i̶n̶g̶ ̶o̶n̶ ̶t̶h̶e̶ ̶c̶l̶i̶e̶n̶t̶ ̶s̶i̶d̶e̶ ̶o̶n̶l̶y̶.
但是 process
对象在 Webpack5 和 NextJS 中已被弃用,因为它是一个仅用于后端的 NodeJS 变量。
所以我们必须使用来自浏览器的返回 window
对象。
if (typeof window !== "undefined") {
// Client-side-only code
}
其他解决方案是使用 react hook 替换 componentDidMount
:
useEffect(() => {
// Client-side-only code
})
没有SSR
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
在 class Component 的构造函数中,您可以添加
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 = {}
}
}
这将避免错误(在我的情况下,错误会在我单击页面重新加载后发生)。
如果你使用 React Hooks 你可以将代码移动到 Effect Hook:
import * as React from "react";
export const MyComp = () => {
React.useEffect(() => {
// window is accessible here.
console.log("window.innerHeight", window.innerHeight);
}, []);
return (<div></div>)
}
useEffect
里面的代码只在客户端(浏览器)执行,因此可以访问window
.
我在 next.js 中开发 Web 应用程序时遇到了同样的问题这解决了我的问题,您必须参考 window 生命周期方法或反应钩子中的对象。例如,假设我想用 redux 创建一个存储变量,在这个存储中我想使用一个 windows 对象,我可以按如下方式进行:
let store
useEffect(()=>{
store = createStore(rootReducers, window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__())
}, [])
....
所以基本上,当您使用 window 的对象时,总是使用钩子来玩弄或 componentDidMount()
生命周期方法
我必须从 URL 访问哈希,所以我想出了这个
const hash = global.window && window.location.hash;
对于这种情况,Next.js 有 Dynamic Import。
包含仅在浏览器中工作的库的模块,建议使用动态导入。 Refer
错误发生是因为 window 尚不可用,而组件仍在安装中。您可以在安装组件后访问 window 对象。
您可以创建一个非常有用的挂钩来获取动态 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();
日期:2021 年 6 月 8 日
检查 window 对象是否存在,然后跟随代码进行操作。
function getSelectedAddress() {
if (typeof window === 'undefined') return;
// Some other logic
}
您可以定义一个状态变量并使用 window 事件句柄来处理这样的变化。
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 不是未定义的,如果你的页面被重定向到它就不会工作,它只会在加载时工作一次.但是即使页面被重定向到,这种解决方法也有效,而不是在加载时只重定向一次。
我想把我觉得有趣的这种方法留给未来的研究人员。它使用自定义挂钩 useEventListener,可用于许多其他需求。
请注意,您需要对最初发布的内容进行一点更改,就像我建议的那样 here。
所以它会这样结束:
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,
})
如果它是 NextJS 应用程序并且在 _document.js 中,请使用以下内容:
<script dangerouslySetInnerHTML={{
__html: `
var innerHeight = window.innerHeight;
`
}} />
有点晚了,但您也可以考虑使用 next
中的 Dynamic Imports 关闭该组件的 SSR
。
您可以在动态函数内扭曲组件的导入,然后使用返回值作为实际组件。
import dynamic from 'next/dynamic'
const BoardDynamic = dynamic(() => import('../components/Board.tsx'), {
ssr: false,
})
<>
<BoardDynamic />
</>
global?.window && window.innerHeight
使用运算符 ?.
很重要,否则构建命令可能会崩溃。
对于Next.js版本12.1.0,我发现我们可以用process.title
来判断我们是在browser
还是在node
这边。希望对您有所帮助!
export default function Projects(props) {
console.log({ 'process?.title': process?.title });
return (
<div></div>
);
}
1.从终端,我收到{ 'process?.title': 'node' }
2。从 Chrome devtool,我修改 { 'process?.title': 'browser' }
在我的 Next.js 应用程序中,我似乎无法访问 window
:
Unhandled Rejection (ReferenceError): window is not defined
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
将代码从 componentWillMount()
移动到 componentDidMount()
:
componentDidMount() {
console.log('window.innerHeight', window.innerHeight);
}
在 Next.js 中,componentDidMount()
仅在 window
和其他浏览器特定 API 可用的客户端上执行。来自 Next.js wiki:
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()
将在 React v17 中成为 deprecated,因此在不久的将来使用它实际上可能不安全。
componentWillMount()
生命周期挂钩在服务器端和客户端都有效。在您的情况下,服务器在页面服务期间不知道 window
或 document
,建议将代码移至
解决方案一:
componentDidMount()
或者,解决方案 2
如果你只是想在其中表演,那么你可以这样写:
componentWillMount() {
if (typeof window !== 'undefined') {
console.log('window.innerHeight', window.innerHeight);
}
}
̶A̶n̶o̶t̶h̶e̶r̶ ̶s̶o̶l̶u̶t̶i̶o̶n̶ ̶i̶s̶ ̶b̶y̶ ̶u̶s̶i̶n̶g̶ ̶p̶r̶o̶c̶e̶s̶s̶.̶b̶r̶o̶w̶s̶e̶r
̶ ̶t̶o̶ ̶j̶u̶s̶t̶ ̶e̶x̶e̶c̶u̶t̶e̶ ̶ ̶y̶o̶u̶r̶ ̶c̶o̶m̶m̶a̶n̶d̶ ̶d̶u̶r̶i̶n̶g̶ ̶r̶e̶n̶d̶e̶r̶i̶n̶g̶ ̶o̶n̶ ̶t̶h̶e̶ ̶c̶l̶i̶e̶n̶t̶ ̶s̶i̶d̶e̶ ̶o̶n̶l̶y̶.
但是 process
对象在 Webpack5 和 NextJS 中已被弃用,因为它是一个仅用于后端的 NodeJS 变量。
所以我们必须使用来自浏览器的返回 window
对象。
if (typeof window !== "undefined") {
// Client-side-only code
}
其他解决方案是使用 react hook 替换 componentDidMount
:
useEffect(() => {
// Client-side-only code
})
没有SSR
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
在 class Component 的构造函数中,您可以添加
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 = {}
}
}
这将避免错误(在我的情况下,错误会在我单击页面重新加载后发生)。
如果你使用 React Hooks 你可以将代码移动到 Effect Hook:
import * as React from "react";
export const MyComp = () => {
React.useEffect(() => {
// window is accessible here.
console.log("window.innerHeight", window.innerHeight);
}, []);
return (<div></div>)
}
useEffect
里面的代码只在客户端(浏览器)执行,因此可以访问window
.
我在 next.js 中开发 Web 应用程序时遇到了同样的问题这解决了我的问题,您必须参考 window 生命周期方法或反应钩子中的对象。例如,假设我想用 redux 创建一个存储变量,在这个存储中我想使用一个 windows 对象,我可以按如下方式进行:
let store
useEffect(()=>{
store = createStore(rootReducers, window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__())
}, [])
....
所以基本上,当您使用 window 的对象时,总是使用钩子来玩弄或 componentDidMount()
生命周期方法
我必须从 URL 访问哈希,所以我想出了这个
const hash = global.window && window.location.hash;
对于这种情况,Next.js 有 Dynamic Import。
包含仅在浏览器中工作的库的模块,建议使用动态导入。 Refer
错误发生是因为 window 尚不可用,而组件仍在安装中。您可以在安装组件后访问 window 对象。
您可以创建一个非常有用的挂钩来获取动态 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();
日期:2021 年 6 月 8 日
检查 window 对象是否存在,然后跟随代码进行操作。
function getSelectedAddress() {
if (typeof window === 'undefined') return;
// Some other logic
}
您可以定义一个状态变量并使用 window 事件句柄来处理这样的变化。
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 不是未定义的,如果你的页面被重定向到它就不会工作,它只会在加载时工作一次.但是即使页面被重定向到,这种解决方法也有效,而不是在加载时只重定向一次。
我想把我觉得有趣的这种方法留给未来的研究人员。它使用自定义挂钩 useEventListener,可用于许多其他需求。
请注意,您需要对最初发布的内容进行一点更改,就像我建议的那样 here。
所以它会这样结束:
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,
})
如果它是 NextJS 应用程序并且在 _document.js 中,请使用以下内容:
<script dangerouslySetInnerHTML={{
__html: `
var innerHeight = window.innerHeight;
`
}} />
有点晚了,但您也可以考虑使用 next
中的 Dynamic Imports 关闭该组件的 SSR
。
您可以在动态函数内扭曲组件的导入,然后使用返回值作为实际组件。
import dynamic from 'next/dynamic'
const BoardDynamic = dynamic(() => import('../components/Board.tsx'), {
ssr: false,
})
<>
<BoardDynamic />
</>
global?.window && window.innerHeight
使用运算符 ?.
很重要,否则构建命令可能会崩溃。
对于Next.js版本12.1.0,我发现我们可以用process.title
来判断我们是在browser
还是在node
这边。希望对您有所帮助!
export default function Projects(props) {
console.log({ 'process?.title': process?.title });
return (
<div></div>
);
}
1.从终端,我收到{ 'process?.title': 'node' }
2。从 Chrome devtool,我修改 { 'process?.title': 'browser' }