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

window.innerWidth引发Hydration错误,寻求技术解决方案

解决SSR下窗口宽度Hook导致的Hydration不匹配问题

问题根源

服务器端渲染时不存在window对象,你的UseWindowSize Hook初始会返回undefined,而客户端hydrate时,虽然初始状态也是undefined,但useEffect会立刻执行并更新真实窗口宽度,导致客户端首次渲染的UI(基于更新后的宽度)和服务器渲染的UI(基于undefined的判断)不一致,触发类名不匹配和Hydration失败错误。

修复方案

1. 统一初始状态,延迟客户端UI切换

修改Hook,让服务器和客户端的初始状态保持一致,同时在客户端hydrate完成后再更新真实窗口大小。使用Hook的组件需先渲染和服务器一致的占位内容,直到获取到真实窗口宽度后再切换。

2. 修改后的UseWindowSize Hook

import { useState, useEffect, useRef, useCallback } from 'react';

export default function UseWindowSize() {
  const isClient = typeof window === 'object';
  const lastWidth = useRef<number>();
  // 服务器和客户端初始状态统一为null
  const [windowSize, setWindowSize] = useState<number | null>(null);

  const getSize = useCallback((): number => {
    return isClient ? window.innerWidth : 0;
  }, [isClient]);

  useEffect(() => {
    if (!isClient) return;

    // 客户端首次加载立即同步真实窗口大小
    const initialWidth = getSize();
    lastWidth.current = initialWidth;
    setWindowSize(initialWidth);

    function handleResize() {
      const width = getSize();
      if (width !== lastWidth.current) {
        lastWidth.current = width;
        setWindowSize(width);
      }
    }

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

  return windowSize;
};

3. 组件中使用Hook的正确方式

在组件里先判断windowSize是否为null,如果是则渲染和服务器一致的默认布局,直到获取到真实宽度后再调整:

import UseWindowSize from './UseWindowSize';

function Header() {
  const windowSize = UseWindowSize();
  // 初始阶段服务器和客户端统一渲染mobile-header,获取到真实宽度后再切换
  const className = windowSize === null 
    ? 'mobile-header' 
    : windowSize >= 768 ? 'header' : 'mobile-header';

  return <header className={className}>...</header>;
}

额外说明

服务器端无法获取客户端窗口宽度,必须接受服务器渲染默认布局,客户端hydrate完成后再根据真实窗口大小调整UI,以此避免初始渲染的内容不匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 22:57:27