下一页.js 13 “未定义窗口”

Next.js 13 "window is not defined"

提问人:H3lltronik 提问时间:3/10/2023 最后编辑:Chris HamiltonH3lltronik 更新时间:6/12/2023 访问量:8251

问:

我写了一个看起来像这样的组件:

'use client'

export const HorizontalModule = (props: any) => {
    ...

    return (
        {scrollPosition >= 0 && (
            <FirstModule />
          )}

          {scrollPosition >= window.innerHeight * 2 && (
            <SecondModule />
          )}        
    )
})

但是我收到了“窗口未定义”错误。

通过阅读不同的帖子,我发现大多数人发现使用动态导入很有用,所以我在 nextjs 页面的父组件中执行了此操作:

const HorizontalModule = dynamic<any>(
  () => import('./HorizontalModule/HorizontalModule').then((mod) => mod.HorizontalModule),
  {
    ssr: false,
    suspense: true,
    loading: () => <p>Loading...</p>
  }
)

起初我收到此错误:“对象不是函数”

现在我收到“不支持的服务器组件类型:未定义”

我不完全知道我做了什么来切换错误,但它仍然不起作用。

我必须提一下,我在 HorizontalModule 代码中都使用了 window 对象,但当我在 render 函数中使用它时,一切都停止工作。useEffects

我还尝试在组件内部编写如下验证:

if (window === undefined) return (<></>)
return (...)

我收到了相同的窗口未定义对象或水化错误。

我不知道还有什么可做的,ssr false不起作用,悬念也一样,窗口条件......

reactjs next.js next.js13

评论

1赞 Dev AKS 3/10/2023
尝试 if (typeof window !== “undefined”) { ... }
0赞 H3lltronik 3/10/2023
它抛出了水合作用错误

答:

26赞 Chris Hamilton 3/10/2023 #1

来自下一个.js 13 个文档:https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components

[客户端组件] 在服务器上预呈现,并在客户端上冻结。

因此,该指令不会完全在客户端上呈现页面。它仍将在服务器上执行组件代码,就像在 Next.js 12 及更低版本中一样。在使用服务器上不可用的东西时,您需要考虑到这一点。'use client'window

您不能只检查窗口是否已定义,然后立即在客户端上更新,因为这可能会导致服务器预渲染和客户端初始渲染之间不匹配(也称为冻结错误)。

要在客户端加载时更新页面,您需要将 useEffect 钩子与 useState 钩子结合使用。由于是在初始渲染期间执行的,因此状态更新在下一次渲染之前不会生效。因此,第一次渲染与预渲染匹配 - 没有水化错误。更多信息在这里: https://nextjs.org/docs/messages/react-hydration-erroruseEffect

与其在每个需要它的组件中创建此机制,不如创建一个上下文,该上下文只需使用 ,告诉我们可以安全地执行客户端代码。useEffect

是客户端-ctx.jsx

const IsClientCtx = createContext(false);

export const IsClientCtxProvider = ({ children }) => {
  const [isClient, setIsClient] = useState(false);
  useEffect(() => setIsClient(true), []);
  return (
    <IsClientCtx.Provider value={isClient}>{children}</IsClientCtx.Provider>
  );
};

export function useIsClient() {
  return useContext(IsClientCtx);
}

_app.jsx

function MyApp({ Component, pageProps }) {
  return (
    <IsClientCtxProvider>
      <Component {...pageProps} />
    </IsClientCtxProvider>
  );
}

用法

  const isClient = useIsClient();
  return (
    <>
      {scrollPosition >= 0 && <FirstModule />}

      {isClient && scrollPosition >= window.innerHeight * 2 && <SecondModule />}
    </>
  );

现场演示:https://stackblitz.com/edit/nextjs-mekkqj?file=pages/index.tsx

评论

0赞 H3lltronik 3/10/2023
只是为了后代,我的第一个工作解决方案是直接在组件的开头添加窗口验证,这样如果没有窗口,就不会运行代码,但它会向 vscode 记录很多错误。使用你的方法,我用 IsClientProvider 包围了我的 HorizontalModule,正常使用钩子,并且仅在我的 useEffects 和渲染中验证它是否为真。谢谢
0赞 Ben Winding 5/25/2023
干得好,真可惜 next13 不起作用......
0赞 Nick 6/23/2023
获取 .这对我有用:stackoverflow.com/questions/75369036/......ReferenceError: createContext is not defined
0赞 Chris Hamilton 6/23/2023
@Nick .您可以查看现场演示以获取示例。import { createContext } from 'react'
0赞 Chris Hamilton 6/23/2023
@Nick尽管另一个答案很有用,但它完全禁用了该组件的 SSR,即。整个组件在客户端上呈现。此方法允许您在服务器上呈现所有内容,然后在给定条件的客户端上执行更新。
-2赞 Djamilson Alves da Costa 6/12/2023 #2
'use client'

import { useEffect, useState } from 'react'

type IUseStickyHeaderProps = {
   active: boolean
}  

export const useStickyHeader = ():IUseStickyHeaderProps => {
    const [active, setActive] = useState(false)

    useEffect(() => {
        window.addEventListener('scroll', () => {
            if (window.scrollY > 70) {
                setActive(true)
            } else {
                setActive(false)
            }
        })
    }, [])

    return {
        active
    }
}

评论

2赞 Super Kai - Kazuya Ito 6/13/2023
您应该添加解释。