前言
useRouteError
是 React Router v6.4+
引入的關鍵錯誤處理鉤子,用于在 路由錯誤邊界(Error Boundary
) 中獲取路由操作過程中發生的錯誤信息。
它提供了優雅的錯誤處理機制,讓開發者能夠創建用戶友好的錯誤界面。
一、useRouteError 核心用途
1.1、錯誤捕獲:
獲取路由加載、渲染或數據提交過程中發生的錯誤
1.2、錯誤展示:
在錯誤邊界組件中顯示有意義的錯誤信息
1.3、錯誤恢復:
提供錯誤恢復機制(如重試按鈕)
1.4、錯誤分類:
根據錯誤類型顯示不同的UI界面
二、useRouteError 基本用法示例
import { useRouteError } from 'react-router-dom';function ErrorPage() {const error = useRouteError();return (<div id="error-page"><h1>哎呀!出錯了</h1><p>抱歉,發生了一個意外的錯誤。</p><p><i>{error.statusText || error.message}</i></p></div>);
}
三、useRouteError 實際應用場景
3.1、基本錯誤邊界組件
import { useRouteError, isRouteErrorResponse } from 'react-router-dom';export function ErrorBoundary() {const error = useRouteError();// 檢查是否為路由錯誤響應if (isRouteErrorResponse(error)) {return (<div className="error-container"><h1 className="error-title">{error.status} {error.statusText}</h1><p className="error-message">{error.data}</p>{error.status === 404 && (<Link to="/" className="home-link">返回首頁</Link>)}</div>);}// 處理其他類型的錯誤return (<div className="error-container"><h1 className="error-title">未知錯誤</h1><p className="error-message">{error.message || '發生了一個未知錯誤'}</p><button className="retry-btn"onClick={() => window.location.reload()}>重試</button></div>);
}
3.2、分層錯誤邊界
// 根錯誤邊界 - 應用級別錯誤
export function RootErrorBoundary() {const error = useRouteError();return (<div className="fullscreen-error"><div className="error-card"><h1>應用錯誤</h1><p>抱歉,整個應用出現了問題</p><ErrorDetails error={error} /><div className="actions"><button onClick={() => window.location.reload()}>刷新應用</button><a href="/">返回首頁</a></div></div></div>);
}// 路由級別錯誤邊界
export function RouteErrorBoundary() {const error = useRouteError();return (<div className="route-error"><h2>頁面錯誤</h2><ErrorDetails error={error} /><div className="actions"><button onClick={() => window.history.back()}>返回上一頁</button></div></div>);
}// 錯誤詳情組件
function ErrorDetails({ error }) {return (<div className="error-details"><p><strong>錯誤信息:</strong> {error.message}</p>{isRouteErrorResponse(error) && (<p><strong>狀態碼:</strong> {error.status}</p>)}{error.stack && (<details className="stack-trace"><summary>查看技術細節</summary><pre>{error.stack}</pre></details>)}</div>);
}
3.3、在路由配置中使用錯誤邊界
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import {RootLayout,Dashboard,UserProfile,Settings,RootErrorBoundary,RouteErrorBoundary
} from './components';const router = createBrowserRouter([{path: '/',element: <RootLayout />,errorElement: <RootErrorBoundary />, // 應用級錯誤邊界children: [{index: true,element: <Dashboard />},{path: 'profile/:userId',element: <UserProfile />,errorElement: <RouteErrorBoundary />, // 路由級錯誤邊界loader: userProfileLoader // 可能拋出錯誤的loader},{path: 'settings',element: <Settings />,errorElement: <RouteErrorBoundary />,action: settingsAction // 可能拋出錯誤的action}]}
]);function App() {return <RouterProvider router={router} />;
}
3.4、自定義錯誤類型處理
import { useRouteError } from 'react-router-dom';export function ApiErrorBoundary() {const error = useRouteError();// 處理API錯誤if (error.name === 'ApiError') {return (<div className="api-error"><h2>API 請求失敗</h2><p>錯誤代碼: {error.code}</p><p>{error.message}</p>{error.code === 401 ? (<button onClick={() => location.href = '/login'}>重新登錄</button>) : (<button onClick={() => window.location.reload()}>重試</button>)}</div>);}// 處理路由錯誤響應if (isRouteErrorResponse(error)) {switch (error.status) {case 404:return <NotFoundError error={error} />;case 403:return <ForbiddenError error={error} />;case 500:return <ServerError error={error} />;default:return <GenericHttpError error={error} />;}}// 默認錯誤處理return <GenericError error={error} />;
}// 404錯誤組件
function NotFoundError({ error }) {return (<div className="not-found"><h1>頁面未找到</h1><p>您訪問的頁面不存在</p><p>錯誤信息: {error.data || error.statusText}</p><Link to="/" className="home-link">返回首頁</Link></div>);
}// 403錯誤組件
function ForbiddenError({ error }) {return (<div className="forbidden"><h1>權限不足</h1><p>您沒有權限訪問此內容</p><div className="actions"><button onClick={() => window.history.back()}>返回上一頁</button><Link to="/contact">聯系管理員</Link></div></div>);
}
3.5、結合加載器的錯誤處理
// 用戶詳情頁loader
export async function userLoader({ params }) {try {const user = await fetchUser(params.userId);if (!user) {// 拋出404錯誤響應throw new Response('用戶未找到', {status: 404,statusText: 'Not Found'});}return user;} catch (error) {// 拋出自定義API錯誤throw new ApiError({message: '加載用戶數據失敗',code: error.status || 500,originalError: error});}
}// 自定義API錯誤類
export class ApiError extends Error {constructor({ message, code, originalError }) {super(message);this.name = 'ApiError';this.code = code;this.originalError = originalError;// 保留原始堆棧跟蹤if (originalError?.stack) {this.stack = originalError.stack;}}
}
四、useRouteError 高級用法:錯誤恢復機制
import { useRouteError, useNavigate } from 'react-router-dom';export function RecoverableErrorBoundary() {const error = useRouteError();const navigate = useNavigate();const [retryCount, setRetryCount] = useState(0);const handleRetry = () => {setRetryCount(c => c + 1);navigate('.'); // 重新加載當前路由};const handleBack = () => {navigate(-1); // 返回上一頁};return (<div className="recoverable-error"><h1>發生錯誤</h1><p>{error.message || '請稍后再試'}</p><div className="error-actions"><button onClick={handleRetry} className="retry-btn">重試 {retryCount > 0 && `(${retryCount})`}</button><button onClick={handleBack} className="back-btn">返回</button></div>{retryCount > 2 && (<div className="advanced-options"><p>多次重試失敗?</p><button onClick={() => window.location.reload()}>刷新整個頁面</button><Link to="/support">聯系支持</Link></div>)}</div>);
}
五、useRouteError 錯誤類型處理指南
錯誤類型 識別方法 推薦處理方式
404 Not Found
: error.status === 404; 顯示"未找到"頁面,提供返回首頁鏈接
403 Forbidden
: error.status === 403; 顯示權限不足信息,提供登錄或聯系管理員選項
500 Server Error
:error.status === 500;顯示服務器錯誤,提供重試或聯系支持選項
網絡錯誤 :error instanceof TypeError; 顯示連接問題,檢查網絡并提供重試
自定義錯誤: 自定義錯誤類; 根據業務邏輯定制錯誤處理
未捕獲異常: 其他所有錯誤; 顯示通用錯誤信息,提供技術細節
六、useRouteError 最佳實踐
6.1、分層錯誤邊界:
應用級:捕獲整個應用的意外錯誤
路由級:處理特定路由的錯誤
組件級:處理局部UI錯誤
6.2、用戶友好信息:
避免顯示技術性錯誤信息給普通用戶
提供清晰的恢復操作指引
為開發環境保留詳細錯誤日志
6.3、錯誤恢復機制:
提供重試按鈕
實現返回上一頁功能
添加刷新頁面選項
6.4、錯誤日志記錄:
useEffect(() => {if (error) {logErrorToService(error);}}, [error]);
6.5、開發環境增強:
function ErrorDetails({ error }) {return (<div>{process.env.NODE_ENV === 'development' && (<details><summary>開發人員詳情</summary><pre>{error.stack}</pre></details>)}</div>);
}
七、useRouteError 注意事項
7.1、錯誤邊界位置:
錯誤邊界必須位于路由層級中
只能捕獲其子組件樹中的錯誤
7.2、錯誤類型檢查:
使用 isRouteErrorResponse
檢查路由錯誤響應
處理各種可能的錯誤類型
7.3、異步錯誤處理:
loader/action
中的異步錯誤會被自動捕獲
組件中的錯誤需要使用 useRouteError
獲取
7.4、錯誤傳播:
錯誤會向上傳播到最近的錯誤邊界
未被捕獲的錯誤會導致應用崩潰
7.5、與React錯誤邊界結合:
class ReactErrorBoundary extends React.Component {state = { hasError: false };static getDerivedStateFromError() {return { hasError: true };}render() {if (this.state.hasError) {return <RouterErrorBoundary />;}return this.props.children;}
}
總結
useRouteError
是 React Router v6.4+
錯誤處理機制的核心,可以幫助我們實現如下場景:
- 創建用戶友好的錯誤界面
- 實現分層錯誤處理策略
- 根據錯誤類型提供定制化UI
- 實現錯誤恢復和重試機制
- 結合路由加載器和動作進行錯誤處理
通過合理使用 useRouteError
,我們可以顯著提升應用的健壯性和用戶體驗,確保即使發生錯誤,用戶也能獲得清晰的反饋和恢復路徑。