Reach Router中如何保留原URL渲染NotFound组件(含资源不存在场景)
解决方案:组件内处理资源不存在场景,保留原URL
你的核心需求是:无论是全局未匹配的路由,还是/resources/:id下API返回资源不存在的情况,都要渲染<NotFound />组件,同时保留当前URL不变。其实不需要修改路由配置来跳转,而是在ResourceContainer内部处理API请求失败的场景即可。
1. 保留全局404的路由配置
先保留你最初的全局404路由(如果是React Router v6,把default换成path="*"),它会捕获所有未匹配的路由,并且不会修改URL:
React Router v5及更早版本:
<Router> <ResourcesContainer path="/resources" /> <ResourceContainer path="/resources/:id" /> <NotFound default /> {/* 其他路由 */} </Router>
React Router v6:
<Routes> <Route path="/resources" element={<ResourcesContainer />} /> <Route path="/resources/:id" element={<ResourceContainer />} /> <Route path="*" element={<NotFound />} /> </Routes>
2. 在ResourceContainer内部处理资源不存在的情况
当调用GraphQL API发现资源不存在时,不要跳转路由,直接在当前组件内渲染<NotFound />组件,同时传递当前路径给它用于提示信息:
import { useParams, useLocation } from 'react-router-dom'; import { useState, useEffect } from 'react'; function ResourceContainer() { const { id } = useParams(); const location = useLocation(); const [resource, setResource] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isResourceNotFound, setIsResourceNotFound] = useState(false); useEffect(() => { const fetchResource = async () => { setIsLoading(true); try { // 这里替换成你的GraphQL API调用逻辑 const response = await yourGraphQLClient.query({ query: GET_RESOURCE, variables: { id } }); if (!response.data.resource) { // 资源不存在,标记状态 setIsResourceNotFound(true); return; } setResource(response.data.resource); setIsResourceNotFound(false); } catch (error) { // 处理API请求错误,比如网络问题 console.error('Failed to fetch resource:', error); } finally { setIsLoading(false); } }; fetchResource(); }, [id]); // 加载状态 if (isLoading) { return <div>Loading resource...</div>; } // 资源不存在时,直接渲染NotFound组件 if (isResourceNotFound) { return <NotFound currentPath={location.pathname} />; } // 正常渲染资源内容 return ( <div className="resource-container"> <h2>{resource.title}</h2> {/* 其他资源详情内容 */} </div> ); }
3. 优化NotFound组件适配两种场景
修改<NotFound />组件,让它既能接收传递的路径(资源不存在场景),也能自动获取当前路径(全局404场景):
import { useLocation } from 'react-router-dom'; function NotFound({ currentPath }) { const location = useLocation(); // 优先使用传递的路径,否则用当前路由路径 const targetPath = currentPath || location.pathname; return ( <div className="not-found"> <h1>Page Not Found</h1> <p>The page '{targetPath}' was not found.</p> </div> ); }
为什么这个方案可行?
- 全局404路由依然会捕获所有未匹配的URL,保持原地址并渲染NotFound。
- 对于
/resources/:id下的资源不存在场景,我们没有改变路由状态,只是在当前路由的组件内部替换了渲染内容,因此地址栏会保留原本的/resources/xxx路径,同时展示404提示,和全局404的行为完全一致。 - 不需要额外的
/404路由和重定向,避免了URL被修改的问题。
内容的提问来源于stack exchange,提问作者Undistraction
相关产品推荐
相关产品推荐

