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

Next.js App Router中useResponsive Hook的Hydration不匹配修复咨询

修复Next.js App Router中MUI useMediaQuery的Hydration不匹配问题

问题原因

服务端渲染阶段没有浏览器环境,useMediaQuery会返回默认的false;客户端Hydration时,实际计算媒体查询结果返回true,导致服务端渲染的DOM和客户端渲染的DOM结构不一致,触发Hydration不匹配错误。

修复方案

方案1:给useMediaQuery添加noSsr选项

MUI的useMediaQuery支持noSsr配置项,启用后会跳过服务端的媒体查询计算,仅在客户端执行,从根源避免服务端和客户端的初始值差异。

修改useResponsive.ts中的每个useMediaQuery调用,添加{ noSsr: true }参数:

import { useMediaQuery, useTheme } from '@mui/material';
import { useEffect, useLayoutEffect } from 'react';

export interface IBreakpointsType {
  isMobile: boolean;
  isTablet: boolean;
  isSmallLaptop: boolean;
  isLargeLaptop: boolean;
  isDesktop: boolean;
  isComputer: boolean;
  isLaptop: boolean;
}

const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;

const useResponsive = () => {
  const theme = useTheme();
  // 为每个useMediaQuery添加noSsr选项
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'), { noSsr: true });
  const isTablet = useMediaQuery(theme.breakpoints.between('md', 'lg'), { noSsr: true });
  const isSmallLaptop = useMediaQuery(theme.breakpoints.between('lg', 'xl'), { noSsr: true });
  const isLargeLaptop = useMediaQuery(theme.breakpoints.between('xl', 'xxl'), { noSsr: true });
  const isDesktop = useMediaQuery(theme.breakpoints.up('xxl'), { noSsr: true });
  const isComputer = useMediaQuery(theme.breakpoints.up('lg'), { noSsr: true });
  const isLaptop = useMediaQuery(theme.breakpoints.between('md', 'xl'), { noSsr: true });

  useIsomorphicLayoutEffect(() => {
    const handleResize = () => {
      window.dispatchEvent(new Event('resize'));
    };

    window.addEventListener('themeChange', handleResize);

    return () => {
      window.removeEventListener('themeChange', handleResize);
    };
  }, []);

  return {
    isMobile,
    isTablet,
    isSmallLaptop,
    isLargeLaptop,
    isDesktop,
    isComputer,
    isLaptop,
  };
};

export default useResponsive;

方案2:手动管理客户端状态(兼容复杂场景)

如果方案1无法解决问题,可以通过useState初始化服务端安全值,再在客户端挂载后更新为实际媒体查询结果,确保服务端和客户端初始渲染内容一致。

修改后的useResponsive.ts:

import { useTheme } from '@mui/material';
import { useEffect, useState } from 'react';

export interface IBreakpointsType {
  isMobile: boolean;
  isTablet: boolean;
  isSmallLaptop: boolean;
  isLargeLaptop: boolean;
  isDesktop: boolean;
  isComputer: boolean;
  isLaptop: boolean;
}

const useResponsive = () => {
  const theme = useTheme();
  // 初始化服务端安全状态
  const [breakpoints, setBreakpoints] = useState<IBreakpointsType>({
    isMobile: false,
    isTablet: false,
    isSmallLaptop: false,
    isLargeLaptop: false,
    isDesktop: false,
    isComputer: false,
    isLaptop: false,
  });

  useEffect(() => {
    // 客户端挂载后计算实际媒体查询值
    const updateBreakpoints = () => {
      setBreakpoints({
        isMobile: window.matchMedia(theme.breakpoints.down('sm')).matches,
        isTablet: window.matchMedia(theme.breakpoints.between('md', 'lg')).matches,
        isSmallLaptop: window.matchMedia(theme.breakpoints.between('lg', 'xl')).matches,
        isLargeLaptop: window.matchMedia(theme.breakpoints.between('xl', 'xxl')).matches,
        isDesktop: window.matchMedia(theme.breakpoints.up('xxl')).matches,
        isComputer: window.matchMedia(theme.breakpoints.up('lg')).matches,
        isLaptop: window.matchMedia(theme.breakpoints.between('md', 'xl')).matches,
      });
    };

    updateBreakpoints();
    // 监听窗口resize和主题变化事件
    window.addEventListener('resize', updateBreakpoints);
    window.addEventListener('themeChange', updateBreakpoints);

    return () => {
      window.removeEventListener('resize', updateBreakpoints);
      window.removeEventListener('themeChange', updateBreakpoints);
    };
  }, [theme]);

  return breakpoints;
};

export default useResponsive;

额外注意事项

  • 确保使用useResponsive的组件(如Footer.tsx)已经添加'use client'指令,明确标记为客户端组件。
  • 方案1更简洁,优先使用;方案2适合需要自定义状态更新逻辑的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 04:30:18