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

React Router v6打开模态框时背景页回到顶部如何解决

问题根因

React Router v6 默认在路由切换时会自动把页面滚动位置重置到顶部。你通过backgroundLocation保留背景路由的实现思路本身没有问题,但点击链接触发跳转时,实际路由已经切换到模态框对应的路径,默认滚动逻辑会先执行滚回顶部的操作;关闭模态框路由回退时,RRD 又会读取之前缓存的背景页滚动位置重新还原,一来一回就出现了明显的跳顶抖动。

核心解决思路:模态框触发的路由跳转场景下,拦截默认的滚动重置逻辑即可。

适配你现有代码的修复方案

你的代码用的是普通Routes组件的非数据路由写法,不需要重构路由结构,按以下步骤修改即可:

  • 修正路由组件命名冲突问题:你自定义的路由组件和react-router导出的Routes组件同名,会导致标识符报错,先把自定义组件重命名为AppRoutes之类的非冲突名称。
  • 新增滚动逻辑拦截,存在背景路由(即模态框打开场景)时禁止自动滚到顶部:
import React, { useEffect } from "react";
import { Route, Routes, useLocation } from "react-router-dom";
import Home from "./pages/Home";
import Page from "./pages/Page";
import PageVersion from "./pages/PageVersion";
import ModalVersion from "./components/ModalVersion";

const AppRoutes: React.FC = () => {
  const location = useLocation();
  const state = location.state as {
    backgroundLocation?: Location;
  };

  // 拦截默认滚动重置逻辑
  useEffect(() => {
    // 模态框打开场景直接返回,不执行滚动重置
    if (state?.backgroundLocation) return;
    // 普通页面跳转才执行滚动到顶部
    window.scrollTo(0, 0);
  }, [location.pathname, state?.backgroundLocation]);

  return (
    <>
      {/* 背景路由层 */}
      <Routes location={state?.backgroundLocation || location}>
        <Route path={'/'} element={<Home />} />
        <Route path={'/page'} element={<Page />} />
        <Route path={'/page/:more_info'} element={<PageVersion />} />
      </Routes>

      {/* 模态框路由层 */}
      {state?.backgroundLocation && (
        <Routes>
          <Route path={'/page/:more_info'} element={<ModalVersion />} />
        </Routes>
      )}
    </>
  )
}

export default AppRoutes;
  • 修复Page组件的语法错误:
    • 导入语句的form拼写错误改为from
    • 补充useLocation调用获取当前路由实例,避免传入state的backgroundLocation为undefined
import { Link } from "@mui/material"    
import { Link as RouterLink, useLocation } from "react-router-dom";
import APageComponentContainingALink from "./components/APageComponentContainingALink";

const Page: React.FC = () => {
  const location = useLocation();
  return (
   <APageComponentContainingALink>
    <Link
      component={RouterLink}
      to="/page/some_info"
      state={{ backgroundLocation: location }}
    >
      打开详情
    </Link>
   </APageComponentContainingALink>
  )
}
export { Page };
  • 确认模态框滚动锁定配置:

如果你用的是MUI等UI库自带的Modal组件,组件默认内置了背景页滚动锁定逻辑,不需要手动写body样式修改代码,只要不要手动开启disableScrollLock属性即可。如果是自定义Modal组件,在模态框挂载时固定body位置记录滚动偏移,卸载时还原即可,示例代码:

import { useEffect } from "react";
import Modal from "./components/Modal";
import Info from "./components/Info";

const ModalVersion: React.FC = () => {
  useEffect(() => {
    const scrollY = window.scrollY;
    // 固定背景页
    document.body.style.position = 'fixed';
    document.body.style.top = `-${scrollY}px`;
    document.body.style.width = '100%';
    document.body.style.overflow = 'hidden';

    return () => {
      // 关闭时还原
      document.body.style.position = '';
      document.body.style.top = '';
      document.body.style.width = '';
      document.body.style.overflow = '';
      window.scrollTo(0, scrollY);
    }
  }, [])

  return (
    <Modal>
      <Info />
    </Modal>
  )
}
export { ModalVersion };
v6.4+ 数据路由适配方案

如果你后续升级到用createBrowserRouter+RouterProvider的数据路由写法,可以直接用官方提供的滚动恢复hook配置拦截规则,不需要手动写scrollTo逻辑:

import { useScrollRestoration, useLocation } from "react-router-dom";

function ScrollControl() {
  const location = useLocation();
  const state = location.state as { backgroundLocation?: Location };
  useScrollRestoration({
    preventScrollReset: () => !!state?.backgroundLocation
  });
  return null;
}

把该组件放在路由根节点下即可生效。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:24:23