Next.js 13客户端Provider为何在服务端执行?window未定义问题解决
Next.js v13中'use client'组件仍触发window未定义的原因及解决方法
原因
即使标记了'use client',Next.js App Router默认的服务端渲染逻辑依然会让客户端组件的初始化代码(包括useState初始值计算、组件顶层代码)在服务端执行一次——目的是生成可复用的SSR HTML,之后再在客户端进行水合(hydrate)。而服务端环境不存在window对象,直接在useState初始值里调用window.innerWidth/innerHeight自然会触发报错。
解决方法
方法一:用useEffect延迟初始化
useEffect是客户端专属钩子,只会在浏览器环境执行。先给state设默认值,再在useEffect里获取真实窗口尺寸并更新,同时可监听窗口resize事件实现动态更新:
'use client'; import { useState, useEffect, createContext } from 'react'; const WindowContext = createContext({ width: 0, height: 0 }); export function WindowProvider({ children }) { const [windowSize, setWindowSize] = useState({ width: 0, height: 0 }); useEffect(() => { const updateWindowSize = () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); }; // 首次加载时获取尺寸 updateWindowSize(); // 监听窗口大小变化 window.addEventListener('resize', updateWindowSize); // 组件卸载时移除监听 return () => window.removeEventListener('resize', updateWindowSize); }, []); return ( <WindowContext.Provider value={windowSize}> {children} </WindowContext.Provider> ); }
方法二:用typeof window做条件判断
在useState初始值里加入服务端环境判断,让服务端执行时返回默认值,客户端再用真实的window属性:
'use client'; import { useState, createContext } from 'react'; const WindowContext = createContext({ width: 0, height: 0 }); export function WindowProvider({ children }) { const [windowSize, setWindowSize] = useState({ width: typeof window !== 'undefined' ? window.innerWidth : 0, height: typeof window !== 'undefined' ? window.innerHeight : 0, }); useEffect(() => { const updateWindowSize = () => { setWindowSize({ width: window.innerWidth, height: window.innerHeight, }); }; window.addEventListener('resize', updateWindowSize); return () => window.removeEventListener('resize', updateWindowSize); }, []); return ( <WindowContext.Provider value={windowSize}> {children} </WindowContext.Provider> ); }
方法三:用useLayoutEffect(可选)
如果需要在DOM绘制完成前获取窗口尺寸(避免页面闪烁),可以用useLayoutEffect替代useEffect——它会在客户端DOM更新后立即执行,比useEffect更早触发,逻辑和方法一一致,仅替换钩子即可。
内容的提问来源于stack exchange,提问作者Rinor
相关产品推荐
相关产品推荐

