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
问题原因
window is not defined错误:Next.js等SSR框架在服务器端渲染时,全局环境不存在window对象,初始化useState时直接访问window.innerWidth会触发报错。- 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
相关产品推荐
相关产品推荐

