直接访问动态路由时React useEffect钩子无法获取数据问题
问题分析
你的问题根源在于两处关键逻辑错误:
- Context组件的
useEffect中,在异步请求未完成时就调用了setIsLoading(false),导致加载状态提前结束,同时初始渲染时products始终是空数组。 - 直接访问动态路由时,
ProductDetails组件会在数据加载完成前尝试从空数组中查找产品,引发product为undefined的报错。
解决方案
1. 修复Context组件的异步请求逻辑
调整useEffect中的代码,确保只有请求完成(成功或失败)时才更新加载状态,同时新增错误状态用于异常处理:
import { useState, useEffect } from "react"; import ProductContext from "./product-context"; const ProductContextProvider = (props) => { const [Products, setProducts] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); useEffect(() => { const fetchedData = async () => { setIsLoading(true); setError(null); try { const response = await fetch("https://fakestoreapi.com/products/"); if (!response.ok) { throw new Error("Something Went Wrong"); } const data = await response.json(); setProducts(data); } catch (error) { setError(error.message); } finally { // 请求完成(成功/失败)后统一关闭加载状态 setIsLoading(false); } }; fetchedData(); }, []); return ( <ProductContext.Provider value={{ isLoading, products: Products, error }} > {props.children} </ProductContext.Provider> ); }; export default ProductContextProvider;
2. 优化ProductDetails组件的边界情况处理
直接在组件中使用Context,无需通过路由传递props,并处理加载中、错误、产品不存在的场景:
import React, { useContext } from "react"; import { useParams, Navigate } from "react-router-dom"; import NavBar from "../Components/Navigaiton-Bar/NavBar"; import Card from "../UI/Card"; import ProductContext from "../Store/product-context"; const ProductDetails = () => { const params = useParams(); const { isLoading, products, error } = useContext(ProductContext); const productId = +params.productId; const product = products.find(pro => pro.id === productId); // 加载中状态 if (isLoading) { return ( <> <NavBar /> <div className="flex items-center justify-center h-screen"> <p>加载产品详情中...</p> </div> </> ); } // 错误状态 if (error) { return ( <> <NavBar /> <Card> <div className="text-center p-8"> <p>错误:{error}</p> </div> </Card> </> ); } // 产品不存在时跳转到首页 if (!product) { return <Navigate to="/" />; } return ( <> <NavBar /> <Card> <div className="flex items-center justify-center flex-wrap"> <div className="flex items-center w-full h-[100vh] "> <div className="m-10"> <img className="w-[800px] h-[300px]" alt="产品图" src={product.image} ></img> </div> <div> <p>{product.title}</p> <p>${product.price}</p> <p>{product.description}</p> <button>Add To Cart</button> </div> </div> </div> </Card> </> ); }; export default ProductDetails;
3. 简化路由组件
由于ProductDetails直接使用Context,路由组件无需再传递products props:
import { Route, Routes, useLocation } from "react-router-dom"; import { AnimatePresence } from "framer-motion"; import ProductDetails from "../Pages/ProductDetails"; import Home from "../Pages/Home"; const AnimatedRoutes = () => { const location = useLocation(); return ( <AnimatePresence> <Routes location={location} key={location.pathname}> <Route path="/" element={<Home />} /> <Route path="/products/:productId" element={<ProductDetails />} /> </Routes> </AnimatePresence> ); }; export default AnimatedRoutes;
内容的提问来源于stack exchange,提问作者Bharath
相关产品推荐
相关产品推荐

