前言
useRevalidator
是 React Router v6.4+
引入的一個強大鉤子,用于在數據路由(Data Router
)中手動觸發路由數據的重新驗證(revalidation)。
它在需要主動刷新數據而不改變路由位置
的場景中非常有用。
一、useRevalidator 核心用途
手動數據刷新:用戶觸發數據重新加載(如點擊刷新按鈕)
輪詢機制:定期更新數據(如實時儀表盤)
樂觀更新后同步:在本地狀態變更后與服務器同步
外部事件響應:響應 WebSocket
消息或其他外部事件
二、useRevalidator 鉤子返回對象
useRevalidator
返回一個包含兩個屬性的對象:
revalidate
:觸發重新驗證的函數state
:當前重新驗證狀態(idle 或 loading
)
三、useRevalidator 基本用法示例
import { useRevalidator } from 'react-router-dom';function RefreshButton() {const { revalidate, state } = useRevalidator();return (<button onClick={() => revalidate()}disabled={state === 'loading'}>{state === 'loading' ? '刷新中...' : '刷新數據'}</button>);
}
四、useRevalidator 實際應用場景
4.1、用戶手動刷新數據
import { useRevalidator, useLoaderData } from 'react-router-dom';function UserDashboard() {const { users } = useLoaderData();const { revalidate, state } = useRevalidator();const [lastUpdated, setLastUpdated] = useState(new Date());const handleRefresh = () => {revalidate();setLastUpdated(new Date());};return (<div className="dashboard"><div className="dashboard-header"><h1>用戶管理</h1><div className="controls"><button onClick={handleRefresh}disabled={state === 'loading'}className="refresh-btn"><span className="icon">🔄</span>{state === 'loading' ? '加載中...' : '刷新數據'}</button><p className="update-time">最后更新: {lastUpdated.toLocaleTimeString()}</p></div></div><UserList users={users} loading={state === 'loading'} /></div>);
}function UserList({ users, loading }) {if (loading) {return <div className="loading-indicator">加載用戶數據...</div>;}return (<ul className="user-list">{users.map(user => (<li key={user.id} className="user-card"><div className="avatar">{user.name.charAt(0)}</div><div className="user-info"><h3>{user.name}</h3><p>{user.email}</p></div></li>))}</ul>);
}
4.2、實時數據輪詢
import { useEffect } from 'react';
import { useRevalidator } from 'react-router-dom';function RealTimeStockTicker() {const { revalidate } = useRevalidator();// 每15秒自動刷新數據useEffect(() => {const intervalId = setInterval(() => {revalidate();}, 15000);return () => clearInterval(intervalId);}, [revalidate]);return (<div className="ticker">{/* 股票數據展示 */}</div>);
}// 完整示例:股票監控儀表盤
function StockDashboard() {const { stocks } = useLoaderData();const { revalidate, state } = useRevalidator();return (<div className="stock-dashboard"><div className="dashboard-controls"><h2>實時股票行情</h2><div className="auto-refresh"><label><input type="checkbox" onChange={e => setAutoRefresh(e.target.checked)} />自動刷新 (每15秒)</label></div><button onClick={() => revalidate()}className="refresh-btn">手動刷新</button></div>{state === 'loading' ? (<div className="loading-overlay"><div className="spinner"></div><p>更新行情數據...</p></div>) : null}<StockTable stocks={stocks} /></div>);
}
4.3、樂觀更新后重新驗證
import { useRevalidator } from 'react-router-dom';function TodoItem({ todo }) {const { revalidate } = useRevalidator();const [isUpdating, setIsUpdating] = useState(false);const toggleCompleted = async () => {// 樂觀更新:立即更新UIsetIsUpdating(true);try {// 發送API請求await updateTodoStatus(todo.id, !todo.completed);// 成功:重新驗證數據revalidate();} catch (error) {// 錯誤處理console.error('更新失敗:', error);} finally {setIsUpdating(false);}};return (<li className={`todo-item ${todo.completed ? 'completed' : ''}`}><inputtype="checkbox"checked={todo.completed}onChange={toggleCompleted}disabled={isUpdating}/><span className="todo-text">{todo.text}</span>{isUpdating && <span className="updating-indicator">更新中...</span>}</li>);
}
4.4、WebSocket 實時更新
import { useEffect } from 'react';
import { useRevalidator } from 'react-router-dom';function ChatRoom() {const { messages } = useLoaderData();const { revalidate } = useRevalidator();useEffect(() => {const socket = new WebSocket('wss://example.com/chat');socket.addEventListener('message', (event) => {const data = JSON.parse(event.data);if (data.type === 'NEW_MESSAGE') {// 收到新消息時重新驗證數據revalidate();}});return () => socket.close();}, [revalidate]);return (<div className="chat-room"><MessageList messages={messages} /><MessageInput /></div>);
}
五、useRevalidator 高級用法:帶錯誤處理的重試機制
import { useState } from 'react';
import { useRevalidator } from 'react-router-dom';function DataPanel() {const { data, error } = useLoaderData();const { revalidate, state } = useRevalidator();const [retryCount, setRetryCount] = useState(0);const handleRetry = () => {revalidate();setRetryCount(prev => prev + 1);};if (error) {return (<div className="error-panel"><div className="error-message"><h3>數據加載失敗</h3><p>{error.message}</p><p>嘗試次數: {retryCount}</p></div><div className="error-actions"><button onClick={handleRetry}disabled={state === 'loading'}>{state === 'loading' ? '重試中...' : '重試加載'}</button>{retryCount > 2 && (<button onClick={() => window.location.reload()}>刷新頁面</button>)}</div></div>);}return <DataDisplay data={data} />;
}
六、useRevalidator 與相關API對比
方法 用途 特點
useRevalidator
: 用于手動重新驗證路由數據; 具有不改變路由位置,僅刷新數據的特點
useNavigate
: 用于編程式導航; 具有改變路由位置
,觸發新數據加載的特點
useFetcher
: 用于提交數據或加載數據; 具有不觸發導航
,適合局部操作的特點
loader函數
: 用于路由數據加載; 具有自動執行
的路由數據獲取的特點
七、最佳實踐
1、用戶反饋:在重新驗證期間提供加載狀態
2、防抖處理:避免頻繁觸發重新驗證
3、錯誤處理:妥善處理重新驗證失敗情況
4、資源清理:清除輪詢和事件監聽器
5、性能優化:避免不必要的重新驗證
// 帶防抖的刷新按鈕
function DebouncedRefreshButton() {const { revalidate, state } = useRevalidator();const debouncedRevalidate = useDebounce(revalidate, 300);return (<button onClick={debouncedRevalidate}disabled={state === 'loading'}>安全刷新</button>);
}// 自定義防抖鉤子
function useDebounce(callback, delay) {const timerRef = useRef();return (...args) => {clearTimeout(timerRef.current);timerRef.current = setTimeout(() => {callback(...args);}, delay);};
}
八、注意事項
- 數據路由要求:必須在數據路由中使用(使用
createBrowserRouter
等) - 狀態管理:
state 僅
表示重新驗證狀態,不表示初始加載 - 組件卸載:確保在組件卸載時清理輪詢和事件監聽
- 數據變化:重新驗證會觸發所有活動路由的
loader
重新執行 - 錯誤邊界:重新驗證中的錯誤會被路由錯誤邊界捕獲
總結
useRevalidator 是 React Router
數據路由模型中的關鍵工具,它解決了在不改變路由位置的情況下刷新數據的常見需求。通過合理使用此鉤子,我們可以:
a、創建用戶友好的數據刷新機制
b、實現實時數據更新功能
c、構建健壯的樂觀更新流程
d、響應外部數據變化事件
e、提升應用的數據同步能力
此鉤子特別適合需要保持UI與服務器數據同步的復雜應用場景,是構建現代數據驅動應用的強大工具。