自定义钩子在 React 中可不仅仅是一种便利,它们对模块化和可维护代码来说是个大变革。它们允许开发者封装逻辑、管理状态,并以前所未有的方式简化复杂的功能。
(这是一张图片,点击可以查看详细内容。)
React 的强大函数式编程范式重新定义了现代前端开发的格局,为构建模块化、易于维护和可重用的代码铺平了道路。在众多功能中,自定义钩子特别突出,是构建更智能、更简洁组件的关键工具。今天,让我们深入了解每个开发者都应该掌握的一些核心自定义钩子,并学习如何有效地实现它们,从而更好地服务开发工作。
此处省略内容
- useFetch : 简化 API 调用,让开发更轻松 🌐 Fetching 数据是 React 中一个常见的任务。useFetch 这个钩子抽象了重复的逻辑,使之更加简洁,简化了 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 };
}
// this hook is used to fetch data from a specified URL and handle loading and error states
export default useFetch;
点击全屏模式, 点击退出全屏
使用方法
const { data, loading, error } = useFetch<User[]>('/api/users');
if (loading) return <p>正在加载...</p>;
if (error) return <p>错误: {错误信息}</p>;
return <ul>{data?.map(user => <li key={user.id}>{user.name}</li>)}</ul>;
切换到全屏 退出全屏
此处省略了内容
- useDebounce : 性能优化 ⏳ 使用防抖钩子来优化频繁的用户输入,例如搜索或表单字段,减少不必要的渲染和API调用。
实现如下:
导入 { 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;
}
导出默认 useDebounce;
切换到全屏模式,或者退出全屏
怎么用:
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearch = useDebounce(searchTerm, 500);
useEffect(() => {
if (debouncedSearch) {
// 调用API或其他操作
}
}, [debouncedSearch]);
全屏模式,退出全屏
……
- useToggle:使用我们定制的 useToggle 钩子,可以轻松管理模态框、下拉菜单或主题切换的布尔值状态,让你的代码保持干净且易于重用。
实施:
import { useState } from "react";
function useToggle(initialState = false) {
const [state, setState] = useState(initialState);
const toggle = () => setState(prev => !prev);
return [state, toggle];
}
export default useToggle;
点击进入全屏模式,点击退出全屏模式
怎么用:
const [isModalOpen, toggleModal] = useToggle();
return (
<div>
<button onClick={toggleModal}>点击以显示/隐藏模态框</button>
{isModalOpen && <p>模态框</p>}
</div>
);
全屏 / 退出全屏
此处为空
- useLocalStorage : 本地存储数据 📂 利用一个自定义的 useLocalStorage 钩子,存储和获取数据变得既简单又实用。
实现:
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')}>
切换主题
</button>
);
进入全屏 退出全屏
……
- usePrevious :跟踪先前状态 📜 在比较和动画中,跟踪值的先前状态非常必要,可以通过自定义 usePrevious 钩子轻松实现这一功能。
实现部分:
这是一个React钩子函数,名为usePrevious
,它接收一个泛型参数value
,并返回value
的前一个值。这个函数使用了React提供的useEffect
和useRef
钩子。
useRef
用于创建一个引用对象,可以用来保存任何值。在这个函数里,我们创建了一个名为ref
的引用对象,它是一个包含泛型参数T
的类型。useEffect
钩子则在value
发生变化时执行,将value
的当前值赋给ref.current
。
整个函数的作用是在React组件内部追踪value
的前一个值。当调用usePrevious(value)
时,它会返回之前value
的值。如果这是value
第一次被赋值,那么返回值将为undefined
。
export default usePrevious;
表示这个函数是默认导出的,这样其他文件可以导入并使用它。
这是一个简化的解释,适合用于理解代码的功能和用途。
全屏显示 退出全屏
使用方法:
const [count, setCount] = useState(0);
const prevCount = usePrevious(count);
return (
<p>
现在是 {count}, 之前的数值是 {prevCount}
</p>
);
全屏 退出全屏
- useClickOutside : 检测外部点击事件 🖱️ 当点击外部时自动关闭模态框或下拉菜单,使用自定义的 useClickOutside 钩子来以提升用户体验感。
实施:
import { useEffect, useRef } from "react";
// 使用点击外部的自定义Hook
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>下拉菜单</p>}
</div>
);
点击此处进入全屏模式 点击此处退出全屏
- useMediaQuery :使用自定义的 useMediaQuery 钩子可以简化 React 中的媒体查询管理,从而实现更高效的响应式设计。
实施:
import { useState, useEffect } from "react";
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(false);
useEffect(() => {
const mediaQueryList = window.matchMedia(query);
// 使用 matchMedia 检查媒体查询
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 ? '移动视图' : '桌面视图'}</p>;
点击全屏/退出全屏
自定义钩子展示了React的灵活性和强大,使代码更干净、可复用且更容易维护。
通过利用自定义钩子,开发人员可以简化复杂的功能性,并创建可复用且高效的代码。上述示例说明了这些钩子如何优雅地应对常见问题。
希望这对你有帮助!如果你愿意,我们可以在我 LinkedIn 上连接 🚀