React中如何通过Context从初始可能为null的动态URL获取数据
解决方案
核心问题分析
你的问题根源在于三点:
- Provider初始
loading设为true,未区分「是否需要加载数据」的场景 - 当
uri为null时仍会发起无效接口请求,且请求失败后未重置loading状态 - 全局挂载的Provider会在应用启动时就触发加载逻辑,即使用户还未进入详情页
具体修改步骤
1. 修正NewsDetailsContextProvider逻辑
调整loading初始状态,仅在有有效uri时触发加载,并处理异常场景下的状态重置:
import React, {createContext, useEffect, useState} from 'react' import { Loader } from '../ui/Loader'; export const NewsDetailsContext = createContext(); const NewsDetailsProvider = ({children}) =>{ const [newsDetails, setNewsDetailsData] = useState(null); const [loading, setLoading]=useState(false); // 初始设为false,避免全局加载 const [uri,setUri] =useState(null); useEffect(()=>{ async function fetchNewsDetails(){ // uri为空时直接返回,不执行无效请求 if(!uri) return; setLoading(true); // 发起请求前开启加载状态 try{ const res = await fetch(`http://localhost:5173/news/child-detail/${uri}`); if(!res.ok){ throw new Error('Network Error'); } const data = await res.json(); setNewsDetailsData(data); } catch(error){ console.error('Error fetching news details data', error); // 可在此添加错误提示逻辑,比如设置error状态 } finally{ setLoading(false); // 无论成功失败,都结束加载状态 } } fetchNewsDetails(); }, [uri]); // 使用可选链简化取值逻辑 const details = newsDetails?.child_details?.data; const images = newsDetails?.child_details?.images; return ( <NewsDetailsContext.Provider value={{details, images, setUri}}> {loading ? <Loader /> : children} </NewsDetailsContext.Provider> ) } export default NewsDetailsProvider;
2. 优化InnerNews组件逻辑
避免重复调用useContext,并添加局部加载判断:
import React, { useContext, useEffect } from "react"; import { Description } from "../components/ui/Description"; import { useParams } from "react-router-dom"; import { NewsDetailsContext } from "../components/context/NewsDetailsContext"; import { Loader } from "../components/ui/Loader"; export const InnerNews = () => { const { uri } = useParams(); const { setUri, details, images } = useContext(NewsDetailsContext); useEffect(()=>{ if(uri){ setUri(uri); } },[uri, setUri]); // 组件内部判断数据是否加载完成,显示局部加载 if(!details){ return <Loader />; } return ( <> <section> <div className="container mx-auto text-center"> <div className="my-12"> <p className="md:text-4xl text-xl font-extrabold"> {details.post_title} </p> </div> <div className="text-justify md:text-center"> <Description text={details.post_content} /> </div> <div className="grid md:grid-cols-2 gap-8 mt-12"> {images?.map((image) => ( <div key={image.id}> <img src={image.path} alt={`News image ${image.id}`} // 添加有意义的alt文本 className="h-[300px] w-full object-cover" /> </div> ))} </div> </div> </section> </> ); };
3. 可选:缩小Provider作用范围
如果详情页是独立路由,可将NewsDetailsProvider挂载在路由组件内部,而非全局,这样只有进入详情页时才初始化Provider,避免不必要的资源占用:
// App.jsx 路由配置示例 import { Routes, Route } from 'react-router-dom'; import { InnerNews } from './path/to/NewsDetails'; import NewsDetailsProvider from './components/context/NewsDetailsContext'; function App() { return ( <Routes> {/* 其他路由 */} <Route path="/news/:uri" element={ <NewsDetailsProvider> <InnerNews /> </NewsDetailsProvider> } /> </Routes> ); }
内容的提问来源于stack exchange,提问作者Sangam Katwal
相关产品推荐
相关产品推荐

