文章中英模式
布魯斯前端JS面試題目 - 實作自定義 useFetch Hook
學習如何實作 React 中的自定義 useFetch Hook,掌握數據獲取、加載狀態管理、錯誤處理及快取機制,提升前端應用的用戶體驗。
文章中英模式
懶得看文章?那就來看影片吧
自定義 useFetch Hook 概述
在 React 應用中,數據獲取是一個常見需求。自定義 useFetch Hook 可以封裝數據獲取邏輯,包括加載狀態、錯誤處理,甚至可以實現快取和請求去重等高級功能,從而簡化組件代碼並提高可復用性。
為何需要自定義 useFetch Hook?
- 1. 關注點分離:將數據獲取邏輯與視圖邏輯分開
- 2. 代碼復用:避免在多個組件中重複編寫相似的數據獲取邏輯
- 3. 一致性處理:統一處理加載狀態、錯誤和響應
- 4. 功能擴展:可以方便地添加快取、防抖、節流等功能
- 5. 測試簡化:使組件和數據獲取邏輯更容易獨立測試
基本的 useFetch Hook 實作
下面是一個基本的 useFetch Hook 實作,包含數據獲取、加載狀態和錯誤處理:
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// 重置狀態
setLoading(true);
setData(null);
setError(null);
// 獲取數據
fetch(url)
.then(response => {
// 檢查響應狀態
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(json => {
setData(json);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, [url]); // 當 URL 變化時重新獲取數據
return { data, loading, error };
}
// 使用示例
function UserProfile({ userId }) {
const { data, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!data) return null;
return (
<div>
<h1>{data.name}</h1>
<p>Email: {data.email}</p>
</div>
);
}
這個基本實作提供了三個關鍵狀態:數據、加載狀態和錯誤信息,使組件能夠根據這些狀態渲染不同的 UI。
取消請求的 useFetch 實作
在實際應用中,當組件卸載或依賴項變化時,我們需要取消進行中的請求以避免內存洩漏和狀態更新錯誤。以下是一個使用 AbortController 實現請求取消的 useFetch Hook:
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
// 創建 AbortController 實例
const controller = new AbortController();
const signal = controller.signal;
// 重置狀態
setLoading(true);
setData(null);
setError(null);
// 獲取數據
fetch(url, { signal })
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(json => {
setData(json);
setLoading(false);
})
.catch(err => {
// 忽略因取消請求而產生的錯誤
if (err.name === 'AbortError') {
console.log('Fetch aborted');
} else {
setError(err.message);
setLoading(false);
}
});
// 清理函數:組件卸載或依賴項變化時取消請求
return () => {
controller.abort();
};
}, [url]);
return { data, loading, error };
}
這個改進版本使用了 AbortController API 來取消進行中的請求。當組件卸載或 URL 變化時,清理函數會自動執行,取消尚未完成的請求,有效防止內存洩漏和在已卸載組件上設置狀態的問題。