跳到主要内容

useWindowResize

监听窗口大小变化的 Hook。

基本用法

实时编辑器
function Demo() {
  const [size, setSize] = useState({ width: 0, height: 0 });

  useWindowResize(() => {
    setSize({
      width: window.innerWidth,
      height: window.innerHeight,
    });
  });

  return (
    <div>
      <div
        style={{
          padding: '24px',
          backgroundColor: 'var(--ifm-color-emphasis-100)',
          borderRadius: '8px',
          textAlign: 'center',
        }}
      >
        <div style={{ fontSize: '48px', marginBottom: '16px' }}>📐</div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px' }}>
          <div
            style={{
              padding: '16px',
              backgroundColor: 'var(--ifm-background-surface-color)',
              borderRadius: '6px',
            }}
          >
            <div style={{ fontSize: '32px', fontWeight: 'bold', color: 'var(--ifm-color-primary)' }}>
              {size.width}
            </div>
            <div style={{ fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
              宽度 (px)
            </div>
          </div>
          <div
            style={{
              padding: '16px',
              backgroundColor: 'var(--ifm-background-surface-color)',
              borderRadius: '6px',
            }}
          >
            <div style={{ fontSize: '32px', fontWeight: 'bold', color: 'var(--ifm-color-primary)' }}>
              {size.height}
            </div>
            <div style={{ fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
              高度 (px)
            </div>
          </div>
        </div>
        <div style={{ marginTop: '12px', fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
          调整浏览器窗口大小查看变化
        </div>
      </div>
    </div>
  );
}
结果
Loading...

响应式布局

根据窗口大小切换布局:

实时编辑器
function Demo() {
  const [layout, setLayout] = useState('desktop');

  useWindowResize(() => {
    const width = window.innerWidth;
    if (width < 768) {
      setLayout('mobile');
    } else if (width < 1024) {
      setLayout('tablet');
    } else {
      setLayout('desktop');
    }
  });

  const layoutConfig = {
    mobile: { icon: '📱', name: '移动端', color: 'var(--ifm-color-primary)' },
    tablet: { icon: '📲', name: '平板', color: 'var(--ifm-color-success)' },
    desktop: { icon: '🖥️', name: '桌面端', color: 'var(--ifm-color-info)' },
  };

  const config = layoutConfig[layout];

  return (
    <div
      style={{
        padding: '24px',
        backgroundColor: 'var(--ifm-color-emphasis-100)',
        borderRadius: '8px',
        textAlign: 'center',
      }}
    >
      <div style={{ fontSize: '64px', marginBottom: '12px' }}>{config.icon}</div>
      <div style={{ fontSize: '24px', fontWeight: 'bold', marginBottom: '8px', color: config.color }}>
        {config.name}
      </div>
      <div style={{ fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
        当前布局类型
      </div>
    </div>
  );
}
结果
Loading...

侧边栏自动收起

小屏幕时自动收起侧边栏:

实时编辑器
function Demo() {
  const [sidebarOpen, setSidebarOpen] = useState(true);

  useWindowResize(() => {
    if (window.innerWidth < 1024) {
      setSidebarOpen(false);
    } else {
      setSidebarOpen(true);
    }
  });

  return (
    <div>
      <Button onClick={() => setSidebarOpen(!sidebarOpen)} style={{ marginBottom: '12px' }}>
        {sidebarOpen ? '收起' : '展开'} 侧边栏
      </Button>
      <div
        style={{
          display: 'grid',
          gridTemplateColumns: sidebarOpen ? '200px 1fr' : '1fr',
          gap: '12px',
          minHeight: '200px',
        }}
      >
        {sidebarOpen && (
          <div
            style={{
              padding: '16px',
              backgroundColor: 'var(--ifm-color-emphasis-100)',
              borderRadius: '8px',
            }}
          >
            <div style={{ fontWeight: 'bold', marginBottom: '12px' }}>侧边栏</div>
            <div style={{ fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
              导航菜单项...
            </div>
          </div>
        )}
        <div
          style={{
            padding: '16px',
            backgroundColor: 'var(--ifm-background-surface-color)',
            border: '1px solid var(--ifm-color-emphasis-300)',
            borderRadius: '8px',
          }}
        >
          <div style={{ fontWeight: 'bold', marginBottom: '8px' }}>主内容区</div>
          <div style={{ fontSize: '14px', color: 'var(--ifm-color-emphasis-700)' }}>
            窗口宽度小于 1024px 时,侧边栏会自动收起
          </div>
        </div>
      </div>
    </div>
  );
}
结果
Loading...

计算网格列数

根据窗口宽度动态调整网格列数:

实时编辑器
function Demo() {
  const [columns, setColumns] = useState(4);

  useWindowResize(() => {
    const width = window.innerWidth;
    if (width < 640) setColumns(1);
    else if (width < 768) setColumns(2);
    else if (width < 1024) setColumns(3);
    else setColumns(4);
  });

  return (
    <div>
      <div
        style={{
          marginBottom: '12px',
          padding: '12px',
          backgroundColor: 'var(--ifm-color-primary-lightest)',
          borderRadius: '6px',
          textAlign: 'center',
        }}
      >
        当前列数: <strong>{columns}</strong>
      </div>
      <div
        style={{
          display: 'grid',
          gridTemplateColumns: `repeat(${columns}, 1fr)`,
          gap: '12px',
        }}
      >
        {Array.from({ length: 8 }, (_, i) => (
          <div
            key={i}
            style={{
              padding: '20px',
              backgroundColor: 'var(--ifm-color-emphasis-100)',
              borderRadius: '6px',
              textAlign: 'center',
            }}
          >
            {i + 1}
          </div>
        ))}
      </div>
    </div>
  );
}
结果
Loading...

API

参数

function useWindowResize(
callback: () => void
): void
参数说明类型默认值
callback窗口大小变化时的回调函数() => void-

返回值

无返回值。

特性

  • 自动清理: 组件卸载时自动移除监听器
  • 性能优化: 内部已做防抖处理
  • 类型安全: 完整的 TypeScript 支持

与 useWindowEvent 的区别

特性useWindowResizeuseWindowEvent
事件类型仅 resize任意窗口事件
使用便捷性✅ 专门优化需指定事件类型
性能内置防抖需手动处理
// useWindowResize - 推荐用于窗口大小监听
useWindowResize(() => {
console.log('resized');
});

// useWindowEvent - 更通用
useWindowEvent('resize', () => {
console.log('resized');
});

性能优化建议

使用防抖

虽然 useWindowResize 内部已有优化,但对于复杂计算,建议结合 useDebouncedValue

function Demo() {
const [size, setSize] = useState({ width: 0, height: 0 });
const [debouncedSize] = useDebouncedValue(size, 300);

useWindowResize(() => {
setSize({
width: window.innerWidth,
height: window.innerHeight,
});
});

// 使用 debouncedSize 进行复杂计算
}

避免频繁渲染

只在必要时更新状态:

// ❌ 不推荐 - 每次 resize 都更新
useWindowResize(() => {
setWidth(window.innerWidth);
});

// ✅ 推荐 - 只在跨越阈值时更新
useWindowResize(() => {
const width = window.innerWidth;
const newIsMobile = width < 768;
if (newIsMobile !== isMobile) {
setIsMobile(newIsMobile);
}
});

使用场景

  • 响应式布局: 根据屏幕大小调整布局
  • 侧边栏控制: 小屏幕自动收起侧边栏
  • 网格系统: 动态调整列数
  • 图表适配: 重新计算图表尺寸
  • 虚拟列表: 更新可视区域大小
  • 工具提示: 调整提示位置避免溢出

注意事项

  • 回调函数应该尽量轻量,避免复杂计算
  • 考虑使用防抖减少不必要的更新
  • 在回调中访问 DOM 时注意性能
  • 不要在回调中进行大量状态更新