在我的Next.js应用程序中,我似乎无法访问窗口:
未处理的拒绝(ReferenceError):没有定义窗口
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
在我的Next.js应用程序中,我似乎无法访问窗口:
未处理的拒绝(ReferenceError):没有定义窗口
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
当前回答
我在一个自定义钩子中包装了一般的解决方案(if (typeof window === 'undefined')返回;),这让我非常满意。它有一个类似的界面来反应useMemo钩子,我真的很喜欢。
import { useEffect, useMemo, useState } from "react";
const InitialState = Symbol("initial");
/**
*
* @param clientFactory Factory function similiar to `useMemo`. However, this function is only ever called on the client and will transform any returned promises into their resolved values.
* @param deps Factory function dependencies, just like in `useMemo`.
* @param serverFactory Factory function that may be called server side. Unlike the `clientFactory` function a resulting `Promise` will not be resolved, and will continue to be returned while the `clientFactory` is pending.
*/
export function useClientSideMemo<T = any, K = T>(
clientFactory: () => T | Promise<T>,
deps: Parameters<typeof useMemo>["1"],
serverFactory?: () => K
) {
const [memoized, setMemoized] = useState<T | typeof InitialState>(
InitialState
);
useEffect(() => {
(async () => {
setMemoized(await clientFactory());
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, deps);
return typeof window === "undefined" || memoized === InitialState
? serverFactory?.()
: memoized;
}
使用的例子:
我使用它来动态导入与next.js中的SSR不兼容的库,因为它自己的动态导入只与组件兼容。
const renderer = useClientSideMemo(
async () =>
(await import("@/components/table/renderers/HighlightTextRenderer"))
.HighlightTextRendererAlias,
[],
() => "text"
);
正如你所看到的,我甚至实现了一个回退工厂回调,所以你可以提供一个结果时,最初呈现在服务器上。在所有其他方面,这个钩子应该表现类似于react useMemo钩子。接受反馈。
其他回答
我必须从URL访问哈希,所以我想到了这个
const hash = global.window && window.location.hash;
我想把我觉得有趣的方法留给未来的研究人员。它使用了一个自定义钩子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])
}
这是我做过的一个简单的解决方法。
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"要好,因为如果你只是检查窗口不是undefined,如果你的页面被重定向到,它不会工作,它只在加载时工作一次。但是,即使页面被重定向到,这个解决方法也可以工作,而不仅仅是在加载时一次。
您可以定义一个状态变量并使用窗口事件句柄来处理这样的更改。
const [height, setHeight] = useState();
useEffect(() => {
if (!height) setHeight(window.innerHeight - 140);
window.addEventListener("resize", () => {
setHeight(window.innerHeight - 140);
});
}, []);
在类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 = {}
}
}
这将避免错误(在我的例子中,错误将在我单击重载页面后发生)。