You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js开发遇window未定义及Hydration失败问题求助

设备方向检测的React/Next.js问题解决

问题描述

做设备方向检测实验时遇到两个问题:

  • 初始代码直接访问window对象,抛出ReferenceError: window is not defined
  • 添加typeof window !== "undefined"判断后,出现Hydration失败错误,提示服务器与客户端初始UI不匹配

初始代码

import React, { useState, useEffect } from 'react';

export default function App() {
  const [isLandscape, setIsLandscape] = useState(window.innerWidth > window.innerHeight);

 useEffect(() => {
  function handleResize() {
  setIsLandscape(window.innerWidth > window.innerHeight);
  }

  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
 }, []);

 return (
   <div>
  {isLandscape ? <Landscape /> : <Portrait />}
   </div>
);
}

修改后代码(引发Hydration错误)

export default function App() {
        
    if (typeof window !== "undefined") {

        const [isLandscape, setIsLandscape] = useState(window.innerWidth > window.innerHeight);

         useEffect(() => {
          function handleResize() {
            setIsLandscape(window.innerWidth > window.innerHeight);
          }

          window.addEventListener('resize', handleResize);
          return () => window.removeEventListener('resize', handleResize);
         }, []);

         return (
           <div>
            {isLandscape ? <Landscape /> : <Portrait />}
           </div>
        );
        
    }
        
}

错误信息

Error: Hydration failed because the initial UI does not match what was rendered on the server.
See more info here: https://nextjs.org/docs/messages/react-hydration-error

问题原因

  1. window is not defined错误:Next.js等SSR框架在服务器端渲染时,全局环境不存在window对象,初始化useState时直接访问window.innerWidth会触发报错。
  2. Hydration失败:修改后的代码在服务器端渲染时,typeof window !== "undefined"为false,组件返回undefined;但客户端hydrate时会渲染实际的div和方向组件,导致服务端与客户端初始渲染的UI结构完全不一致,触发Hydration错误。

解决方案

方案1:初始状态用默认值,客户端挂载后更新

import React, { useState, useEffect } from 'react';

export default function App() {
  // 服务器端渲染时用默认值,保证服务端与客户端初始结构一致
  const [isLandscape, setIsLandscape] = useState(false);

  useEffect(() => {
    // 客户端挂载后,先执行一次获取当前方向
    const updateOrientation = () => {
      setIsLandscape(window.innerWidth > window.innerHeight);
    };
    updateOrientation();
    
    // 监听窗口 resize 事件
    window.addEventListener('resize', updateOrientation);
    
    // 组件卸载时移除监听
    return () => window.removeEventListener('resize', updateOrientation);
  }, []);

  return (
    <div>
      {isLandscape ? <Landscape /> : <Portrait />}
    </div>
  );
}

原理:服务器端渲染时组件返回包含Portrait的结构,客户端挂载后再更新为实际的方向状态,保证服务端与客户端初始渲染的DOM结构一致,避免Hydration冲突。

方案2:Next.js 13+ 客户端组件标记

如果你使用Next.js 13+ App Router,直接给组件添加'use client'指令,让组件仅在客户端渲染:

'use client'; // 标记为客户端组件,跳过服务器端渲染
import React, { useState, useEffect } from 'react';

export default function App() {
  const [isLandscape, setIsLandscape] = useState(window.innerWidth > window.innerHeight);

  useEffect(() => {
    const handleResize = () => {
      setIsLandscape(window.innerWidth > window.innerHeight);
    };
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return (
    <div>
      {isLandscape ? <Landscape /> : <Portrait />}
    </div>
  );
}

原理:'use client'指令让组件只在客户端执行,不会在服务器端渲染,因此可以直接访问window对象,同时不存在服务端与客户端渲染不一致的问题。


内容的提问来源于stack exchange,提问作者ndisn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 09:11:15