文章中英模式
布鲁斯前端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 变化时,清理函数会自动执行,取消尚未完成的请求,有效防止内存泄漏和在已卸载组件上设置状态的问题。