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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 01:38:13