在我的Next.js应用程序中,我似乎无法访问窗口:
未处理的拒绝(ReferenceError):没有定义窗口
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
在我的Next.js应用程序中,我似乎无法访问窗口:
未处理的拒绝(ReferenceError):没有定义窗口
componentWillMount() {
console.log('window.innerHeight', window.innerHeight);
}
当前回答
这是我做过的一个简单的解决方法。
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,如果你的页面被重定向到,它不会工作,它只在加载时工作一次。但是,即使页面被重定向到,这个解决方法也可以工作,而不仅仅是在加载时一次。
其他回答
日期:06/08/2021
检查窗口对象是否存在,然后跟随代码进行操作。
function getSelectedAddress() {
if (typeof window === 'undefined') return;
// Some other logic
}
将代码从componentWillMount()移动到componentDidMount():
componentDidMount() {
console.log('window.innerHeight', window.innerHeight);
}
在Next.js中,componentDidMount()只在提供窗口和其他浏览器特定api的客户端上执行。来自Next.js wiki:
js是通用的,这意味着它首先在服务器端执行代码, 然后客户端。窗口对象只在客户端显示,因此if 你绝对需要在一些React组件中访问它 应该把该代码放在componentDidMount中。这个生命周期方法 只能在客户端执行。你可能还想检查一下是否有 是不是有其他的通用库可以满足你的需要。
同样,componentWillMount()将在React的v17中被弃用,因此在不久的将来使用它实际上可能是不安全的。
我在一个自定义钩子中包装了一般的解决方案(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钩子。接受反馈。
对于这种情况,Next.js具有动态导入功能。
对于包含只在浏览器中工作的库的模块,建议使用动态导入。请参考
这是我做过的一个简单的解决方法。
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,如果你的页面被重定向到,它不会工作,它只在加载时工作一次。但是,即使页面被重定向到,这个解决方法也可以工作,而不仅仅是在加载时一次。