Next.js 13“窗口未定义”

H3l*_*nik 19 reactjs next.js next.js13

我写了一个如下所示的组件:

'use client'

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

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

          {scrollPosition >= window.innerHeight * 2 && (
            <SecondModule />
          )}        
    )
})
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

我必须提到,我在 Horizo​​ntalModule 代码中使用了窗口对象,但当useEffects我在渲染函数中使用它时,所有的都停止工作。

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

if (window === undefined) return (<></>)
return (...)
Run Code Online (Sandbox Code Playgroud)

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

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

Chr*_*ton 29

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

[客户端组件] 在服务器上预渲染并在客户端上进行水化。

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

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

要在客户端加载时更新页面,您需要结合使用useEffecthook 和useStatehook。由于useEffect在初始渲染期间执行,因此状态更新直到下一次渲染才会生效。因此,第一个渲染与预渲染匹配 - 没有水合错误。更多信息在这里: https: //nextjs.org/docs/messages/react-Hydration-error

您无需在每个需要它的组件中创建此机制,而是可以创建一个仅使用 using 设置布尔值的上下文useEffect,告诉我们可以安全地执行客户端代码。

is-客户端-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);
}
Run Code Online (Sandbox Code Playgroud)

_app.jsx

function MyApp({ Component, pageProps }) {
  return (
    <IsClientCtxProvider>
      <Component {...pageProps} />
    </IsClientCtxProvider>
  );
}
Run Code Online (Sandbox Code Playgroud)

用法

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

      {isClient && scrollPosition >= window.innerHeight * 2 && <SecondModule />}
    </>
  );
Run Code Online (Sandbox Code Playgroud)

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