React博客详情页:ID不存在时无法跳转至404页面问题求助
问题分析与修复方案
核心问题
- 状态初始化与未处理报错:初始
myData设为{},当找不到对应ID的博客时,data.find()返回undefined,执行setData(undefined)后,myData变为undefined,此时访问myData.id会抛出Cannot read properties of undefined (reading 'id')的错误,导致重定向逻辑无法触发。 - 依赖项冗余:第一个
useEffect的依赖数组中加入了setData和setIsLoading,这两个是React内置的状态更新函数,本身稳定无需作为依赖,会引发不必要的重复渲染。 - 不符合单页应用的重定向方式:使用
window.location.replace会刷新页面,应该用React Router提供的useNavigate钩子实现无刷新跳转。 - 无效异步日志:
setData是异步操作,之后立刻console.log(myData)打印的是旧状态,没有实际意义。
修复后的代码
import { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; // 引入useNavigate import './styles/detail.css' import PostHero from '../components/design/PostHero/PostHero'; import HistoryButton from '../components/design/HistertButton/HistoryButton'; const Detail = () => { // 初始设为null,清晰区分未加载和未找到的状态 const [myData, setData] = useState(null); const { id } = useParams(); const [isLoading, setIsLoading] = useState(true); const navigate = useNavigate(); // 初始化导航钩子 useEffect(() => { let isActive = true; fetch('../blogs.json') .then(response => { // 处理请求失败的情况 if (!response.ok) throw new Error('请求失败'); return response.json(); }) .then(data => { if (isActive) { const foundPost = data.find(post => post.id == id); setData(foundPost); setIsLoading(false); } }) .catch(err => { // 捕获请求错误,比如JSON文件不存在的情况 console.error(err); setIsLoading(false); }) return () => isActive = false; }, [id]); // 移除冗余依赖 useEffect(() => { if (!isLoading) { // myData为null或undefined时,说明未找到博客,执行重定向 if (!myData) { navigate('/not-found'); } } }, [myData, isLoading, navigate]); // 加载状态显示提示,避免页面空白 if (isLoading) { return <div>加载中...</div>; } // 未找到博客时返回null,此时已触发重定向 if (!myData) { return null; } return ( <div className='blog-detail'> <PostHero id={myData.id}/> <div className='post-container'> <div> <div className='content-header'> <h2 className='post-title'>{myData.titel}</h2> <HistoryButton /> </div> <span className='post-date'>{myData.datum}</span> <ul className='post-tags'> {myData.tags?.map((tag,index) => ( <div className='li-item' key={index}> <div className='li-indicator' style={{backgroundColor: `var(--${tag})`}}/> <li>{tag}</li> </div> ))} </ul> </div> <div className='content-container'> <div className='post-tekst'> {myData.description} </div> <div className='detail-photos'> {myData.detail_images?.map((img, index) => ( <div className='detail-image' key={index}> <img src={`../../../${img}`} alt="博客详情图"/> </div> ))} </div> </div> </div> </div> ) } export default Detail;
关键修改说明
- 状态初始化调整:将
myData初始值改为null,明确区分“未加载”和“未找到博客”的状态。 - 依赖项优化:只保留
id作为第一个useEffect的依赖,避免不必要的重渲染。 - 正确重定向方式:使用
useNavigate替代window.location.replace,符合单页应用的路由跳转规范。 - 错误处理增强:添加请求失败的捕获逻辑,同时在渲染阶段增加加载提示,提升用户体验。
- 简化条件判断:直接通过
!myData判断是否未找到博客,避免访问myData.id导致的报错。
内容的提问来源于stack exchange,提问作者Stef Verniers
相关产品推荐
相关产品推荐

