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

React页面跳转返回不丢失数据与滚动位置的实现方案

解决方案:React分页列表跳转返回后恢复状态(无全量数据存储)

针对分页加载的列表页跳转详情页返回后,需要保留已加载数据、恢复滚动位置且不存储全量数据的需求,以下是两种简洁的React实现方案:

方案一:基于sessionStorage的临时缓存(推荐,支持页面刷新)

利用sessionStorage存储已加载的分页数据、当前页码和滚动位置,组件挂载时自动恢复状态,避免重复请求已加载过的数据。

代码实现

import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

const PAGE_SIZE = 10; // 每页数据量

function ListPage() {
  const navigate = useNavigate();
  const [listData, setListData] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);

  // 组件挂载时恢复状态
  useEffect(() => {
    // 读取缓存的列表数据、页码和滚动位置
    const savedData = sessionStorage.getItem('listPageData');
    const savedPage = sessionStorage.getItem('listCurrentPage');
    const savedScroll = sessionStorage.getItem('listScrollPosition');

    if (savedData) {
      setListData(JSON.parse(savedData));
    }
    if (savedPage) {
      setCurrentPage(Number(savedPage));
    }
    // 恢复滚动位置
    if (savedScroll) {
      window.scrollTo(0, Number(savedScroll));
    } else {
      // 首次加载时请求第一页数据
      fetchPage(1);
    }
  }, []);

  // 状态更新时同步缓存到sessionStorage
  useEffect(() => {
    sessionStorage.setItem('listPageData', JSON.stringify(listData));
    sessionStorage.setItem('listCurrentPage', currentPage.toString());
  }, [listData, currentPage]);

  // 监听滚动,实时记录滚动位置
  useEffect(() => {
    const handleScroll = () => {
      sessionStorage.setItem('listScrollPosition', window.scrollY.toString());
    };
    window.addEventListener('scroll', handleScroll);
    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  // 请求指定页码的分页数据
  const fetchPage = async (page) => {
    const response = await fetch(`/api/list?page=${page}&size=${PAGE_SIZE}`);
    const newData = await response.json();
    // 追加新数据到现有列表
    setListData(prev => [...prev, ...newData]);
    setCurrentPage(page);
  };

  // 加载更多按钮逻辑
  const handleLoadMore = () => {
    fetchPage(currentPage + 1);
  };

  // 跳转到详情页
  const handleGoToDetail = (itemId) => {
    navigate(`/detail/${itemId}`);
  };

  return (
    <div>
      <ul>
        {listData.map(item => (
          <li key={item.id} onClick={() => handleGoToDetail(item.id)}>
            {item.title}
          </li>
        ))}
      </ul>
      <button onClick={handleLoadMore}>加载更多</button>
    </div>
  );
}

export default ListPage;

方案优势

  • 仅缓存已加载的分页数据,不会存储全量数据,适配生产环境大数据量场景
  • sessionStorage为标签页隔离存储,多标签页场景互不干扰
  • 支持页面刷新后恢复状态,稳定性更强

方案二:基于React Router状态传递(临时跳转场景)

如果不需要页面刷新后保留状态,可以通过useNavigate的state参数传递当前列表状态,返回时直接读取路由状态恢复。

代码实现

import { useState, useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';

const PAGE_SIZE = 10;

function ListPage() {
  const navigate = useNavigate();
  const location = useLocation();
  const [listData, setListData] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);

  // 从路由状态恢复数据
  useEffect(() => {
    if (location.state?.listData) {
      setListData(location.state.listData);
      setCurrentPage(location.state.currentPage);
      window.scrollTo(0, location.state.scrollPosition);
    } else {
      fetchPage(1);
    }
  }, [location.state]);

  const fetchPage = async (page) => {
    const response = await fetch(`/api/list?page=${page}&size=${PAGE_SIZE}`);
    const newData = await response.json();
    setListData(prev => [...prev, ...newData]);
    setCurrentPage(page);
  };

  const handleLoadMore = () => {
    fetchPage(currentPage + 1);
  };

  // 跳转时携带当前状态
  const handleGoToDetail = (itemId) => {
    navigate(`/detail/${itemId}`, {
      state: {
        listData,
        currentPage,
        scrollPosition: window.scrollY
      }
    });
  };

  // 其余渲染逻辑同方案一
  return (
    <div>
      <ul>
        {listData.map(item => (
          <li key={item.id} onClick={() => handleGoToDetail(item.id)}>
            {item.title}
          </li>
        ))}
      </ul>
      <button onClick={handleLoadMore}>加载更多</button>
    </div>
  );
}

export default ListPage;

方案特点

  • 无需依赖本地存储,状态随路由跳转临时携带
  • 页面刷新后状态会丢失,适合不需要持久化的临时跳转场景

注意事项

  1. sessionStorage有大小限制(通常为5MB),如果单页数据量较大,可设置最大缓存页数,超过后删除最早加载的页面数据
  2. 若需要跨标签页共享状态,可替换为localStorage,但需注意清理过期数据
  3. 可封装自定义Hook(如usePagedListCache)复用缓存和状态恢复逻辑,减少代码冗余

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:32:04