在我的Next.js应用程序中,我似乎无法访问窗口:

未处理的拒绝(ReferenceError):没有定义窗口

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


当前回答

对于那些不能使用hook的人(例如,函数组件):

使用setTimeout(() => yourFunctionWithWindow());将允许它获得窗口实例。我想只是还需要一点时间来加载。

其他回答

我在一个自定义钩子中包装了一般的解决方案(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钩子。接受反馈。

没有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

这是我做过的一个简单的解决方法。

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,如果你的页面被重定向到,它不会工作,它只在加载时工作一次。但是,即使页面被重定向到,这个解决方法也可以工作,而不仅仅是在加载时一次。

在类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。