Next.js分类页面路径切换时产品列表不更新问题求助
Next.js 分类页面路由切换时产品列表不更新的修复方案
问题描述
开发基于Next.js的React项目,产品与多级分类数据通过API获取并展示在/categories/[slug].jsx页面,包含分类详情及对应产品列表:
- 直接访问分类页面(如
http://localhost/categories/category-one)时,数据加载正常,功能运行良好 - 从页面侧边栏点击其他分类链接(如
http://localhost/categories/category-two)时,URL路径和分类详情已更新,但产品列表仍显示旧分类内容,需手动刷新页面才能加载正确数据 - 尝试使用
useEffect监听路径变化但未解决问题
提供的代码
/categories/[slug].jsx
import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import ProductArchive from '/ProductArchive'; import { getCategory, getCategoryByHierarchy, getCategorySlugs } from '/libs/categories'; import { getProductsByCatIds, getProductsCountByCatIds } from '/libs/products'; const CategoryScreen = ({ category, productsByCatIds, productsCount, categories }) => { const [products, setProducts] = useState(productsByCatIds); const [hasMore, setHasMore] = useState(true); const router = useRouter(); const getMoreProducts = async () => { const moreProducts = await getProductsByCatIds({ catId: category.term_id, perPage: 16, offset: products.length, }); if (moreProducts.code === 'no_product') return; setProducts((products) => [...products, ...moreProducts]); }; useEffect(() => { console.log('path got changed'); }, [router.asPath]); useEffect(() => { setHasMore(productsCount > products.length ? true : false); }, [productsCount, products]); return ( <> <ProductArchive products={products} getMoreProducts={getMoreProducts} hasMore={hasMore} productsCount={productsCount} banner={{ heading: category.name, i2Text: category.name }} data={categories} /> </> ); }; export const getStaticPaths = async () => { const paths = await getCategorySlugs(); return { paths, fallback: 'blocking', }; }; export const getStaticProps = async ({ params }) => { const category = await getCategory(params.slug); if (!category) { return { notFound: true, }; } const productsByCatIds = await getProductsByCatIds({ catId: category.id, perPage: 16, }); const productsCount = await getProductsCountByCatIds({ catId: category.id, }); const categories = await getCategoryByHierarchy(); const data = { category, productsByCatIds, productsCount, categories }; return { props: data || {}, revalidate: 1, }; }; export default CategoryScreen;
/ProductArchive.jsx
import Link from 'next/link'; import DefaultBanner from './DefaultBanner'; import ProductCard from './ProductCard'; import Sidebar from './Sidebar'; import InfiniteScroll from 'react-infinite-scroll-component'; import DefaultLoader from './DefaultLoader'; import { thousandSeparator } from '../../libs/functions/thousandSeparator'; const ProductArchive = ({ banner, products, getMoreProducts, hasMore, productsCount, data }) => { return ( <section> <DefaultBanner heading={banner.heading} i2Text={banner.i2Text} /> <div className="container"> <div className="grid-view"> <div className="col-1"> <Sidebar data={data} /> </div> <div className="col-2"> <div className="relative"> {productsCount > 0 && ( <small className="total"> Total: {thousandSeparator(productsCount)} </small> )} </div> {productsCount > 0 ? ( <InfiniteScroll dataLength={products.length} next={getMoreProducts} hasMore={hasMore} loader={<DefaultLoader />} endMessage={<p></p>} scrollThreshold="0.5" > <div className="another-grid"> {products.length > 0 ? ( products.map((product, index) => { return ( <ProductCard key={product.id} index={index} product={product} /> ); }) ) : ( <div> <h4 className="text-xl">Oops... No products found.</h4> <p className="mt-2 font-thin"> <Link href="/"> <a>Go back to homepage</a> </Link> </p> </div> )} </div> </InfiniteScroll> ) : ( <div>No products found.</div> )} </div> </div> </div> </section> ); }; export default ProductArchive;
问题根源
Next.js在客户端路由切换时,静态生成页面的getStaticProps不会重新执行,但组件会接收新的props。而你将products状态初始化为productsByCatIds后,没有在新props传入时更新该状态,导致产品列表始终停留在初始值。此外,getMoreProducts函数可能捕获旧的category实例,导致加载更多时仍使用旧分类ID。
修复方案
修改/categories/[slug].jsx组件,添加状态同步逻辑并优化函数依赖:
- 导入
useCallback钩子 - 用
useCallback包裹getMoreProducts,确保它始终使用最新的分类ID和产品长度 - 添加
useEffect监听productsByCatIds变化,重置产品状态为新分类的初始产品 - 优化
hasMore的重置逻辑,确保路由切换后正确判断是否还有更多产品
修改后的完整代码如下:
import { useRouter } from 'next/router'; import { useEffect, useState, useCallback } from 'react'; // 新增useCallback import ProductArchive from '/ProductArchive'; import { getCategory, getCategoryByHierarchy, getCategorySlugs } from '/libs/categories'; import { getProductsByCatIds, getProductsCountByCatIds } from '/libs/products'; const CategoryScreen = ({ category, productsByCatIds, productsCount, categories }) => { const [products, setProducts] = useState(productsByCatIds); const [hasMore, setHasMore] = useState(true); const router = useRouter(); // 用useCallback包裹,依赖最新的category.term_id和products.length const getMoreProducts = useCallback(async () => { const moreProducts = await getProductsByCatIds({ catId: category.term_id, perPage: 16, offset: products.length, }); if (moreProducts.code === 'no_product') { setHasMore(false); return; } setProducts((prevProducts) => [...prevProducts, ...moreProducts]); }, [category.term_id, products.length]); // 监听productsByCatIds变化,重置产品状态为新分类的初始产品 useEffect(() => { setProducts(productsByCatIds); }, [productsByCatIds]); // 监听productsCount和products变化,更新hasMore状态 useEffect(() => { setHasMore(productsCount > products.length); }, [productsCount, products]); return ( <> <ProductArchive products={products} getMoreProducts={getMoreProducts} hasMore={hasMore} productsCount={productsCount} banner={{ heading: category.name, i2Text: category.name }} data={categories} /> </> ); }; // 下面的getStaticPaths和getStaticProps保持不变 export const getStaticPaths = async () => { const paths = await getCategorySlugs(); return { paths, fallback: 'blocking', }; }; export const getStaticProps = async ({ params }) => { const category = await getCategory(params.slug); if (!category) { return { notFound: true, }; } const productsByCatIds = await getProductsByCatIds({ catId: category.id, perPage: 16, }); const productsCount = await getProductsCountByCatIds({ catId: category.id, }); const categories = await getCategoryByHierarchy(); const data = { category, productsByCatIds, productsCount, categories }; return { props: data || {}, revalidate: 1, }; }; export default CategoryScreen;
修复说明
- 状态同步:通过
useEffect监听productsByCatIds的变化,当路由切换导致新的分类产品数据传入时,自动重置products状态为新分类的初始产品列表 - 函数稳定性:用
useCallback包裹getMoreProducts,确保它始终引用最新的category.term_id和products.length,避免加载更多时使用旧分类ID - 逻辑优化:简化
hasMore的判断逻辑,确保路由切换后能正确判断是否还有更多产品可加载
内容的提问来源于stack exchange,提问作者Minn Khant Naing
相关产品推荐
相关产品推荐

