自定义 Hook:提取可复用逻辑
将有状态逻辑移入自定义的 use* 函数,在组件之间共享行为,避免重复代码或提升状态。
自定义 Hook:提取可复用逻辑 是 CoddyKit 上的免费 Frontend Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Frontend Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Frontend Academy 课程共包含 4 节课。
什么是自定义钩子
自定义钩子是一个名称以 use 开头、并在内部调用其他钩子的 JavaScript 函数。自定义钩子可以提取有状态逻辑,让多个组件共享这些逻辑,而无需复制粘贴。
将逻辑提取到自定义钩子中
找出组件之间重复的逻辑(获取数据、加载状态和错误处理,窗口大小,本地存储),并将其移入自定义钩子。
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handler = () => setWidth(window.innerWidth);
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);
return width;
}
// Usage:
const width = useWindowWidth();
const isMobile = width < 768;useFetch——数据获取钩子
一种可复用的数据获取钩子,任何组件都可以使用它从 URL 加载数据。
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
async function load() {
try {
const res = await fetch(url, { signal: controller.signal });
if (!res.ok) throw new Error(`${res.status}`);
setData(await res.json());
} catch (err) {
if ((err as DOMException).name !== 'AbortError')
setError((err as Error).message);
} finally { setLoading(false); }
}
load();
return () => controller.abort();
}, [url]);
return { data, loading, error };
}useLocalStorage 钩子
一种自定义钩子,可以将值同步到 localStorage,并通过存储事件保持不同标签页之间的数据同步。
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
const set = useCallback((newValue: T) => {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
}, [key]);
return [value, set] as const;
}自定义钩子共享逻辑,而不是状态
每个调用自定义钩子的组件都会获得自己独立的状态。钩子不是单例——它们只是用于创建相互隔离状态实例的函数调用。
命名约定
自定义钩子的名称必须以 use 开头。ESLint 的 react-hooks 代码检查规则会强制执行这一点,并确保钩子在顶层且按照正确顺序调用。
返回多个值
自定义钩子通常返回一个对象(包含命名值)或一个元组(类似 useState)。元组允许使用自定义名称进行解构;对象则能更清晰地说明每个值的含义。
// Tuple (like useState):
const [value, setValue] = useLocalStorage('key', 0);
// Object (more descriptive):
const { data, loading, error, refetch } = useFetch('/api/users');useDebounce 钩子
对值进行防抖:只有在用户停止输入指定的延迟时间后,才更新该值。
function useDebounce<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
// Usage:
const debouncedSearch = useDebounce(searchTerm, 300);
useEffect(() => { search(debouncedSearch); }, [debouncedSearch]);useMediaQuery 钩子
将 window.matchMedia 封装到钩子中,以响应式地读取 CSS 媒体查询结果。
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(() => window.matchMedia(query).matches);
useEffect(() => {
const mq = window.matchMedia(query);
const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
mq.addEventListener('change', handler);
return () => mq.removeEventListener('change', handler);
}, [query]);
return matches;
}测试自定义钩子
使用 @testing-library/react 的 renderHook() 工具,可以在不将自定义钩子包装到组件中的情况下单独测试它。
import { renderHook, act } from '@testing-library/react';
test('useCounter increments', () => {
const { result } = renderHook(() => useCounter(0));
expect(result.current.count).toBe(0);
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});已发布的钩子库
社区钩子库提供了数百个经过测试的钩子:@uidotdev/usehooks、react-use、usehooks-ts。在编写自定义钩子之前,请先检查是否已经存在经过充分测试的版本。
快速检查
当两个不同的组件调用同一个自定义钩子时,该钩子内部的状态会发生什么?
总结:自定义钩子
自定义钩子提取有状态逻辑以便复用。名称使用 use 前缀。每个调用该钩子的组件都会获得独立的状态。可以返回对象或元组。常见模式包括:useFetch、useLocalStorage、useDebounce、useMediaQuery。使用 renderHook() 进行测试。编写新钩子前,请先检查社区库中是否已有可用实现。
常见问题解答
「自定义 Hook:提取可复用逻辑」课时是免费的吗?
是的 — 「自定义 Hook:提取可复用逻辑」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Frontend Academy 课程的其余内容,请升级到 CoddyKit PRO。 Frontend Academy 课程共包含 4 节课。
「自定义 Hook:提取可复用逻辑」这节课中我会学到什么?
将有状态逻辑移入自定义的 use* 函数,在组件之间共享行为,避免重复代码或提升状态。 你通过在浏览器中直接运行的动手代码来练习 Frontend Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Frontend Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Frontend Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「自定义 Hook:提取可复用逻辑」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Frontend Academy 课中编写并运行代码吗?
能。每节 Frontend Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。