React实现返回上一页时定位到原滚动位置的方案
在ReactJS中实现页面返回时精准定位到滚动位置的方案
下面提供几种实用的实现思路,覆盖普通列表和虚拟列表场景:
方法一:利用路由状态存储滚动位置
通过React Router的state属性传递跳转前的滚动位置,返回页面时读取该状态并恢复滚动:
代码示例(React Router v6)
import { useNavigate, useLocation } from 'react-router-dom'; const PageA = () => { const navigate = useNavigate(); const location = useLocation(); // 点击列表行跳转时保存滚动位置 const handleRowClick = (rowId) => { const currentScrollY = window.scrollY; navigate(`/detail/${rowId}`, { state: { scrollY: currentScrollY } }); }; // 组件挂载/路由状态更新时恢复滚动位置 useEffect(() => { if (location.state?.scrollY) { window.scrollTo(0, location.state.scrollY); } }, [location]); return ( <div className="long-list"> {/* 超长列表渲染,示例为1000行 */} {Array.from({ length: 1000 }).map((_, index) => ( <div key={index} className="list-row" onClick={() => handleRowClick(index)} > 第{index + 1}行 - 点击查看详情 </div> ))} </div> ); };
如果用Link组件,可直接通过to属性的state传递:
<Link to={{ pathname: `/detail/${rowId}`, state: { scrollY: window.scrollY } }} > 查看详情 </Link>
方法二:虚拟列表场景的精准定位
如果页面A使用虚拟列表(如react-window/react-virtualized),直接存储窗口滚动位置可能不准确,需保存目标行的索引,通过虚拟列表自身方法滚动:
代码示例(react-window)
import { FixedSizeList as List } from 'react-window'; import { useNavigate, useLocation } from 'react-router-dom'; import { useRef, useEffect } from 'react'; const PageA = () => { const navigate = useNavigate(); const location = useLocation(); const listRef = useRef(null); // 跳转时保存目标行索引 const handleRowClick = (rowIndex) => { navigate(`/detail/${rowIndex}`, { state: { targetRowIndex: rowIndex } }); }; // 返回时滚动到目标行 useEffect(() => { if (location.state?.targetRowIndex && listRef.current) { listRef.current.scrollToItem(location.state.targetRowIndex); } }, [location]); return ( <List ref={listRef} height={600} itemCount={1000} itemSize={50} width="100%" > {({ index, style }) => ( <div style={style} className="virtual-list-row" onClick={() => handleRowClick(index)} > 虚拟列表第{index + 1}行 </div> )} </List> ); };
方法三:用sessionStorage持久化滚动位置
若担心路由状态丢失(比如用户刷新页面),可使用sessionStorage存储滚动信息,返回后读取并清除:
import { useNavigate } from 'react-router-dom'; import { useEffect } from 'react'; const PageA = () => { const navigate = useNavigate(); const handleRowClick = (rowId) => { const scrollY = window.scrollY; sessionStorage.setItem('pageAScrollPos', scrollY.toString()); navigate(`/detail/${rowId}`); }; useEffect(() => { const savedPos = sessionStorage.getItem('pageAScrollPos'); if (savedPos) { window.scrollTo(0, parseInt(savedPos)); sessionStorage.removeItem('pageAScrollPos'); } }, []); // 列表渲染逻辑... };
注意事项
- 如果页面A存在路由参数变化(如筛选条件更新),需增加判断逻辑,避免在参数变更时错误恢复旧的滚动位置。
- 虚拟列表场景优先使用组件自身的滚动方法,比
window.scrollTo更精准,因为虚拟列表仅渲染可见区域DOM。
内容的提问来源于stack exchange,提问作者user2495545
相关产品推荐
相关产品推荐

