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

React分页状态丢失问题:从书籍详情返回后页码重置为1

分页页码状态丢失问题:返回详情页后回到第一页而非原页码

我开发了一个从后端获取书籍数据并实现分页功能的网站,分页功能本身运行正常,但存在一个问题:当我点击第二页的书籍进入详情页后,点击返回按钮会回到分页的第一页,而非之前的第二页。我需要实现分页当前页码状态的保存,确保返回时能回到原页码。


Shop组件代码

import React, { useEffect, useState } from 'react'
import Footer from '../GlobalComponents/Footer'
import Navbar from '../GlobalComponents/Navbar'
import { RatingStar } from "rating-star";
import courses_bg from '../../Images/Banners/others_bg.jpg'
import api from '../../AxiosInstance/api'
import { useDispatch, useSelector } from 'react-redux'
import { BookActions } from '../../Redux/Actions/Actions'
import { Link } from 'react-router-dom';

const Shop = () => {
    const [pageNumber, setPageNumber] = useState(0)
    const [totalNumOfPages, setTotalNumOfPages] = useState(0)
    const dispatch = useDispatch()
    const AllShopBooks = useSelector(state => state.book.books)
    const pages = new Array(totalNumOfPages).fill(null).map((v, i) => i)
    useEffect(() => {
        const getBooks = async () => {
            try {
                const { data } = await api.get(`/book/get-books?page=${pageNumber}`)
                setTotalNumOfPages(data.totalPage)
                dispatch(BookActions(data.book))
            } catch (error) {
                console.log(error)
            }
        }
        getBooks()
    }, [dispatch, pageNumber])

    const handlePageNum = (pageIndex) => {
        setPageNumber(pageIndex)
    }

    return (
        <div>
            <Navbar />
            <section className='h-[50vh] lg:h-[90vh]' style={{
                backgroundImage: `url(${courses_bg})`,
                backgroundPosition: 'center center ', backgroundSize: "cover", backgroundRepeat: "no-repeat"
            }}>
                <div className='flex justify-center items-center h-full'>
                    <h1 className='text-white text-3xl lg:text-6xl '>Our Shop</h1>
                </div>
            </section>
            <section className='bg-[#f6f6f6] h-max py-20 px-10'>
                <div>
                    <div className='grid mt-5 grid-cols-1 justify-items-center gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>
                        {AllShopBooks?.map((item, index) => (
                            <Link to={`/shop/${item._id}`} state={{ item }} className='mx-2 text-decoration-none' key={index} >
                                <div className='w-max pr-4 bg-white py-4 '>
                                    <img src={item.image} alt='' />
                                    <div className='mx-3 my-2 '>
                                        <h1 className='text-lg font-bold text-[#444444] !font-sans'>{item.title}</h1>
                                        <span className='text-sm '>Writer:
                                            <span className='font-bold ml-2'>{item.writer}</span>
                                        </span>
                                        <div className='flex items-center'>
                                            <span className='flex gap-2'>
                                                <span className='text-[#777777] font-semibold'>Price:</span>
                                                <span className='text-[#f6393e] font-bold text-base'>{item.price}</span>
                                            </span>
                                            <span><RatingStar id="different-maximum-score" rating={item.rating} maxScore={100} /></span>
                                        </div>
                                    </div>
                                </div>
                            </Link>
                        ))}
                    </div>
                </div>

                <div className="flex justify-center mt-9">
                    <ul className='flex cursor-pointer text-[#8a8a8a]'>
                        {pages.map((pageIndex, index) => (
                            <li key={index} className={`px-4 py-2 border ${pageNumber === pageIndex ? "bg-[#cdcdcd] font-semibold" : ""}`} onClick={() => handlePageNum(pageIndex)}>{pageIndex + 1}</li>
                        ))}
                    </ul>
                </div>

            </section>
            <section>
                <Footer />
            </section>
        </div>
    )
}

export default Shop

BookDetails组件代码

import React, { useEffect, useState } from 'react'
import Footer from '../GlobalComponents/Footer'
import Navbar from '../GlobalComponents/Navbar'
import courses_bg from '../../Images/Banners/others_bg.jpg'
import { useLocation } from 'react-router-dom'
import { RatingStar } from "rating-star";
import { AiOutlineMinus, AiOutlinePlus } from 'react-icons/ai'
import { useDispatch, useSelector } from 'react-redux'
import { useNavigate } from 'react-router-dom'
import { FeedBookActions } from '../../Redux/Actions/Actions'
import api from '../../AxiosInstance/api'

const BookDetails = () => {
    const navigate = useNavigate()
    const location = useLocation()
    const dispatch = useDispatch()
    const AllBooks = useSelector(state => state.book.feedBooks)
    const [count, setCount] = useState(0)
    const { title, writer, price, desc, rating, image } = location.state.item

    useEffect(() => {
        const getBooks = async () => {
            try {
                const { data } = await api.get("/book/feed-books")
                console.log(data)
                dispatch(FeedBookActions(data))
            } catch (error) {
                console.log(error)
            }
        }
        getBooks()
    }, [dispatch])
    const toDetailComponent = (title, item) => {
        navigate(`/book/${title}`, { state: { item } })
    }
    return (
        <div>
            <Navbar />
            <section className='h-[50vh] lg:h-[90vh]' style={{
                backgroundImage: `url(${courses_bg})`,
                backgroundPosition: 'center center ', backgroundSize: "cover", backgroundRepeat: "no-repeat"
            }}>
                <div className='flex justify-center items-center h-full'>
                    <h1 className='text-white text-3xl lg:text-6xl '>Book Details</h1>
                </div>
            </section>
            <section>
                <div className='mx-10 flex flex-col lg:flex-row gap-5 py-20'>
                    <div className='w-full md:h-[40vh] lg:h-[70vh] flex justify-center items-center bg-[#e6e6e6]'>
                        <img className='text-center h-min' src={image} alt='' />
                    </div>
                    <div className='w-full flex flex-col gap-2'>
                        <h1 className='text-[#002147]'>{title}</h1>
                        <div>
                            <span className='text-justify text-[#8a8a8a]'>{desc}</span>
                        </div>
                        <div className='flex gap-3'>
                            <span>Writer:</span>
                            <span className='text-[#002147] font-semibold'>{writer}</span>
                        </div>
                        <div className='flex gap-3'>
                            <span>Price:</span>
                            <span className='text-[#002147] font-semibold'>{price}</span>
                        </div>
                        <div className='flex items-center'>
                            <span>Rating:</span>
                            <RatingStar id="different-maximum-score" rating={rating} maxScore={100} />
                        </div>
                        <div className='flex lg:items-center lg:gap-5 flex-col lg:flex-row '>
                            <div className='flex gap-2 h-min items-center'>
                                <span className='bg-[#f0f0f0] py-2 px-3 text-xl cursor-pointer' onClick={() => setCount(count - 1)}><AiOutlineMinus /></span>
                                <span className='bg-[#ffffff] py-2 px-3 text-xl font-semibold'>{ count}</span>
                                <span className='bg-[#f0f0f0] py-2 px-3 text-xl cursor-pointer' onClick={() => setCount(count + 1)}><AiOutlinePlus /></span>
                            </div>
                            <div>
                                <button className='my-4 w-36 px-4 py-2 bg-[#002147] text-[#fdc800] font-semibold'>Add to Cart</button>
                            </div>
                        </div>
                    </div>
                </div>
            </section>
            <section className='bg-[#f6f6f6] py-20 px-10'>
                <div className='flex flex-col justify-center items-center h-full'>
                    <h1 className='text-[#002147] text-2xl lg:text-4xl '>Our Shop</h1>
                    <p className='leading-8 text-center'>Belis nisl adipiscing sapien sed malesu diame lacus eget erat  Cras mollis scelerisqu <br></br>Nullam arcu liquam here was consequat.</p>
                </div>
                <div className='grid mt-5 grid-cols-1 justify-items-center gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>
                    {AllBooks?.map((item, index) => (
                        <div className='mx-2' key={index} onClick={() => toDetailComponent(item.title, item)}>
                            <div className='w-max pr-4 bg-white py-4 '>
                                <img src={item.image} alt='' />
                                <div className='mx-3 my-2 '>
                                    <h1 className='text-lg font-bold text-[#444444] !font-sans'>{item.title}</h1>
                                    <span className='text-sm '>Writer:
                                        <span className='font-bold ml-2'>{item.writer}</span>
                                    </span>
                                    <div className='flex items-center'>
                                        <span className='flex gap-2'>
                                            <span className='text-[#777777] font-semibold'>Price:</span>
                                            <span className='text-[#f6393e] font-bold text-base'>{item.price}</span>
                                        </span>
                                        <span><RatingStar id="different-maximum-score" rating={item.rating} maxScore={100} /></span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    ))}
                </div>
            </section>
            <section>
                <Footer />
            </section>
        </div>
    )
}

export default BookDetails

解决方法

方案1:通过URL参数保存页码(推荐)

这种方式能同时支持返回恢复页码和页面刷新保留状态,符合URL语义化设计。

修改Shop组件:

  1. 引入useSearchParams替代useState管理页码
  2. 切换页码时更新URL参数
  3. 监听URL参数变化触发数据请求

修改后的关键代码:

import { Link, useSearchParams } from 'react-router-dom';

const Shop = () => {
    // 从URL参数获取当前页码,默认0
    const [searchParams, setSearchParams] = useSearchParams();
    const pageNumber = parseInt(searchParams.get('page')) || 0;
    const [totalNumOfPages, setTotalNumOfPages] = useState(0)
    const dispatch = useDispatch()
    const AllShopBooks = useSelector(state => state.book.books)
    const pages = new Array(totalNumOfPages).fill(null).map((v, i) => i)

    useEffect(() => {
        const getBooks = async () => {
            try {
                const { data } = await api.get(`/book/get-books?page=${pageNumber}`)
                setTotalNumOfPages(data.totalPage)
                dispatch(BookActions(data.book))
            } catch (error) {
                console.log(error)
            }
        }
        getBooks()
    }, [dispatch, pageNumber])

    const handlePageNum = (pageIndex) => {
        // 更新URL的page参数
        setSearchParams({ page: pageIndex });
    }

    // 其余JSX代码保持不变
}

修改后,URL会显示/shop?page=1这样的格式,返回详情页后,URL参数依然存在,组件会自动加载对应页码的数据。

方案2:通过React Router State传递页码(仅返回有效)

如果不想修改URL,可以在跳转详情页时携带当前页码,返回时读取该状态恢复页码。

步骤1:修改Shop组件的Link跳转

<Link 
  to={`/shop/${item._id}`} 
  state={{ item, currentPage: pageNumber }} 
  className='mx-2 text-decoration-none' 
  key={index}
>

步骤2:在BookDetails组件中返回时携带页码

// 在BookDetails中添加自定义返回逻辑
const goBack = () => {
  const currentPage = location.state?.currentPage || 0;
  navigate('/shop', { state: { prevPage: currentPage } });
}

// 在页面中添加返回按钮,比如Navbar里的返回按钮调用goBack()

步骤3:在Shop组件中读取返回状态

import { useLocation } from 'react-router-dom';

const Shop = () => {
    const location = useLocation();
    // 优先从返回状态取页码,否则默认0
    const [pageNumber, setPageNumber] = useState(location.state?.prevPage || 0);

    // 其余代码保持不变
}

注意:这种方式在刷新Shop页面时,状态会丢失,回到第一页,因此仅适合不需要刷新保留状态的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:20:48