作为前端开发者,我总是会用到的 7 个 React 自定义 Hooks 🚀
自定义 Hooks 不仅仅是 React 中的一项便利功能,它们彻底改变了代码的模块化和可维护性。它们允许开发者封装逻辑、管理状态,并以以前无法实现的方式简化复杂的功能。
React 强大的函数式编程范式重新定义了现代前端开发,为模块化、可维护和可复用的代码铺平了道路。在其众多功能中,自定义 Hooks 脱颖而出,成为构建更智能、更简洁组件的关键工具。今天,我们将深入探讨一些每位开发者都应该掌握的基本自定义 Hooks,并学习如何有效地实现它们。
- useFetch:简化 API 调用 🌐 获取数据是 React 中的常见任务。useFetch hook 抽象了重复的逻辑,简化了 API 调用并优雅地管理状态。
执行:
import { useState, useEffect } from "react";
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
用法:
const { data, loading, error } = useFetch<User[]>('/api/users');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return <ul>{data?.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
- useDebounce:优化性能⏳ 通过防抖钩子可以高效地处理频繁的用户输入,例如搜索或表单字段,从而减少不必要的渲染和 API 调用。
执行:
import { useState, useEffect } from "react";
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(handler);
}, [value, delay]);
return debouncedValue;
}
export default useDebounce;
用法:
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearch = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearch) {
// Trigger API or other actions
}
}, [debouncedSearch]);
- useToggle:轻松管理布尔状态“使用自定义 useToggle 钩子可以轻松管理模态框、下拉菜单或主题切换的切换状态,从而保持代码的简洁性和可重用性。
执行:
import { useState } from "react";
function useToggle(initialState = false) {
const [state, setState] = useState(initialState);
const toggle = () => setState(prev => !prev);
return [state, toggle] as const;
}
export default useToggle;
用法:
const [isModalOpen, toggleModal] = useToggle();
return (
<div>
<button onClick={toggleModal}>Toggle Modal</button>
{isModalOpen && <p>Modal Content</p>}
</div>
);
- useLocalStorage:本地持久化数据📂 通过自定义 useLocalStorage 钩子,从 localStorage 存储和检索数据变得无缝且可重用。
执行:
import { useState } from "react";
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = (value: T) => {
try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue] as const;
}
export default useLocalStorage;
用法:
const [theme, setTheme] = useLocalStorage('theme', 'light');
return (
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
);
- usePrevious:跟踪先前的状态📜 跟踪值的先前状态对于比较和动画至关重要,使用自定义 usePrevious hook 可以轻松实现。
执行:
import { useEffect, useRef } from "react";
function usePrevious<T>(value: T): T | undefined {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
export default usePrevious;
用法:
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<p>
Now: {count}, Before: {prevCount}
</p>
);
- useClickOutside:检测外部点击🖱️ 非常适合在点击外部时关闭模态框或下拉菜单,使用自定义 useClickOutside 钩子以获得更好的用户体验。
执行:
import { useEffect, useRef } from "react";
function useClickOutside(handler: () => void) {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
handler();
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [handler]);
return ref;
}
export default useClickOutside;
用法:
const ref = useClickOutside(() => setDropdownOpen(false));
return (
<div ref={ref}>
{dropdownOpen && <p>Dropdown Content</p>}
</div>
);
- useMediaQuery:处理响应式设计📱 使用自定义 useMediaQuery hook 可以简化 React 中的媒体查询管理,使响应式设计更加高效。
执行:
import { useState, useEffect } from "react";
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);
useEffect(() => {
const mediaQueryList = window.matchMedia(query);
const updateMatch = () => setMatches(mediaQueryList.matches);
updateMatch();
mediaQueryList.addEventListener('change', updateMatch);
return () => mediaQueryList.removeEventListener('change', updateMatch);
}, [query]);
return matches;
}
export default useMediaQuery;
用法:
const isMobile = useMediaQuery('(max-width: 768px)');
return <p>{isMobile ? 'Mobile View' : 'Desktop View'}</p>;
自定义 Hooks 展现了 React 的灵活性和强大功能,能够编写更简洁、更易复用、更易于维护的代码
。
通过利用自定义钩子,开发者可以简化复杂的功能,并创建可重用的高效代码。以上示例展示了这些钩子如何优雅地解决常见难题。
希望这对您有所帮助!如果您能在领英上与我联系,我会非常高兴🚀
文章来源:https://dev.to/joodi/7-react-custom-hooks-i-always-use-as-a-front-end-developer-5i9
