React Router双参数页面直接访问时Redux数据无法获取问题
React Router 单页应用路由参数相关数据加载异常问题
问题现象
- 应用页面数量由JSON商品数据决定:主页通过props传递商品类型,其他页面使用
:section和:page_number双路由参数 - 导航栏点击进入双参数页面时功能正常;但直接在地址栏输入路径或刷新页面时,GoodsList组件无法加载商品列表——组件和路由参数均正常,但Redux的
fetchProducts数据获取逻辑未执行 - 先访问主页再跳转双参数页面时恢复正常;移除
:page_number参数后问题消失
相关代码
Sections组件
import { Routes, Route } from 'react-router-dom'; import GoodsList from './GoodsList'; import Pages from './Pages'; function Sections() { return ( <Routes> <Route path='' element={<><div className="pageContent" data-main="true"><p>Novelties</p><GoodsList goods="novelties" /><p>Hot</p><GoodsList goods="hot" /></div></>} /> <Route path='/:section/:page_number' element={<><div className='pageContent'><GoodsList /><Pages /></div></>} /> </Routes> ) }
Menu组件
import { Link } from 'react-router-dom'; function Menu() { return ( <nav className='menu'> <ul> <li><Link to="">Main</Link></li> <li><Link to="figures/1">Figures</Link></li> <li><Link to="dakimakuras/1">Dakimakuras</Link></li> <li><Link to="manga/1">Manga</Link></li> <li><Link to="other/1">Other merch</Link></li> </ul> </nav> ) }
GoodsList组件
import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { fetchProducts } from '../redux/fetchDataReducer'; import { useParams } from 'react-router-dom'; function GoodsList(props) { const params = useParams(); const goods = returnGoods(); function returnGoods() { if (document.location.pathname == "/") { return props.goods } return params.section } const dispatch = useDispatch(); useEffect(() => { dispatch(fetchProducts()); }, []) const products = useSelector(state => state.fetchDataReducer.products); return ( <div className="list"> {products && products[goods].map((good, i) => ( <div className="productCard" key={i}> <div className="productPicture"> <img src={good.pic} alt="ERROR" /> </div> <p className="productName">{good.name}</p> </div> ))} </div> ) }
Pages组件
import { useSelector } from "react-redux"; import { Link, useParams } from 'react-router-dom'; function Pages() { const params = useParams(); const products = useSelector(state => state.fetchDataReducer.products); const currentProduct = products ? products[params.section] : []; const pageLinks = []; let numberOfPages = () => { if ((currentProduct.length / 8) % 1 == 0) { return currentProduct.length / 8 } else return Math.floor((currentProduct.length / 8) + 1) } for (let x = 1; x < (numberOfPages() + 1); x++) { pageLinks.push(<Link to={`/${params.section}/${x}`} className="pageLink" key={x}>{x}</Link>); } return ( <div className="pageList"> {pageLinks} </div> ) }
问题根源
- useEffect依赖缺失:GoodsList的useEffect仅在组件挂载时执行一次(空依赖数组),直接刷新/地址栏进入时,组件挂载后Redux无商品数据,但后续无重新触发获取的逻辑;而从主页跳转时,主页已触发过
fetchProducts,Redux已有数据,因此能正常渲染。 - 路由参数未作为响应式依赖:路由参数变化(包括首次加载双参数路由)时,useEffect未触发数据获取,导致数据未加载。
- 路径判断不可靠:使用
document.location.pathname判断页面,不如React Router提供的useLocation组件式API可靠,且可能在路由参数未完全解析时就执行判断,导致params.section获取不准确。
修复方案
1. 完善GoodsList组件的响应式逻辑
import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { fetchProducts } from '../redux/fetchDataReducer'; import { useParams, useLocation } from 'react-router-dom'; function GoodsList(props) { const params = useParams(); const location = useLocation(); const dispatch = useDispatch(); // 用useLocation替代document.location,更符合React组件逻辑 const goods = location.pathname === "/" ? props.goods : params.section; useEffect(() => { // 检查Redux中是否已有数据,无数据则触发获取 const existingProducts = useSelector(state => state.fetchDataReducer.products); if (!existingProducts) { dispatch(fetchProducts()); } }, [dispatch, location.pathname, params.section]); // 添加路由相关依赖,确保路由变化时重新执行 const products = useSelector(state => state.fetchDataReducer.products); return ( <div className="list"> {/* 用可选链避免数据未加载时的报错 */} {products && products[goods]?.map((good, i) => ( <div className="productCard" key={i}> <div className="productPicture"> <img src={good.pic} alt={good.name} /> </div> <p className="productName">{good.name}</p> </div> ))} </div> ) }
2. 全局初始化商品数据(可选优化)
在应用入口或根组件中触发数据获取,确保任何路由下都能拿到数据:
// App.js import { useEffect } from 'react'; import { useDispatch } from 'react-redux'; import { fetchProducts } from './redux/fetchDataReducer'; import Menu from './Menu'; import Sections from './Sections'; function App() { const dispatch = useDispatch(); useEffect(() => { dispatch(fetchProducts()); }, [dispatch]); return ( <div className="app"> <Menu /> <Sections /> </div> ); }
3. 优化Pages组件的容错处理
避免数据未加载时出现计算错误:
import { useSelector } from "react-redux"; import { Link, useParams } from 'react-router-dom'; function Pages() { const params = useParams(); const products = useSelector(state => state.fetchDataReducer.products); // 数据未加载时显示加载状态 if (!products || !products[params.section]) { return <div className="pageList">加载中...</div>; } const currentProduct = products[params.section]; const pageLinks = []; // 简化分页计算逻辑 const numberOfPages = Math.ceil(currentProduct.length / 8); for (let x = 1; x <= numberOfPages; x++) { pageLinks.push(<Link to={`/${params.section}/${x}`} className="pageLink" key={x}>{x}</Link>); } return ( <div className="pageList"> {pageLinks} </div> ) }
内容的提问来源于stack exchange,提问作者DanieLSh
相关产品推荐
相关产品推荐

