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

react-router-dom搭配react-spring时浏览器返回恢复滚动位置方案

解决方案

react-spring的转场动画会同时保留入场、出场两个页面的DOM节点直到动画完全结束,浏览器默认的scrollRestoration或者大部分路由自带的滚动恢复逻辑,都是在路由切换瞬间触发,这时候要么入场页面还没完成渲染、要么动画过程中DOM布局还不稳定,才会导致滚动恢复失效,可按以下步骤实现需求:

1. 禁用默认滚动恢复

首先关掉浏览器自带的滚动恢复,避免逻辑冲突,在项目入口文件添加:

if ('scrollRestoration' in window.history) {
  window.history.scrollRestoration = 'manual'
}

2. 页面退场前存储滚动位置

react-spring会等退场动画结束才卸载组件,这时候滚动位置是准确的,在每个页面组件的卸载生命周期将当前滚动位置存入当前history记录的state中,可避免多标签页数据冲突:

import { useHistory } from 'react-router-dom' 

function YourPageComponent() {
  const history = useHistory()

  useEffect(() => {
    // 组件卸载前保存滚动位置
    return () => {
      history.replace({
        ...history.location,
        state: {
          ...history.location.state,
          savedScrollY: window.scrollY
        }
      })
    }
  }, [history])

  // 页面其他逻辑
}

如果是容器内滚动,替换window.scrollY为对应容器DOM的scrollTop即可。

3. 入场动画结束后恢复滚动

在react-spring转场的入场动画完成回调onRest中执行滚动恢复,确保此时入场页面已经完全挂载、布局稳定:

import { useTransition, animated } from '@react-spring/web'
import { useLocation, useHistory, Routes } from 'react-router-dom'

function App() {
  const location = useLocation()
  const history = useHistory()

  const transitions = useTransition(location, {
    from: { opacity: 0, x: '100%' },
    enter: { opacity: 1, x: '0%' },
    leave: { opacity: 0, x: '-100%' },
  })

  return transitions((style, loc) => (
    <animated.div 
      style={{ ...style, position: 'absolute', width: '100%' }}
      // 动画完全结束后触发恢复逻辑
      onRest={() => {
        const savedScrollY = loc.state?.savedScrollY
        if (typeof savedScrollY === 'number') {
          window.scrollTo(0, savedScrollY)
          // 恢复完成后清除存储的滚动位置,避免重复触发
          history.replace({
            ...loc,
            state: {
              ...loc.state,
              savedScrollY: undefined
            }
          })
        }
      }}
    >
      <Routes location={loc}>
        {/* 你的路由配置 */}
      </Routes>
    </animated.div>
  ))
}

额外适配场景

  • 如果页面有异步渲染的内容(比如接口返回的列表),可以把savedScrollY暂存在状态里,等异步内容渲染完成后再执行一次scrollTo即可。
  • 如果使用虚拟滚动组件,直接将存储的滚动位置传给虚拟滚动组件的对应属性即可,不需要操作全局滚动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 16:15:04