自定义 Hook
约 451 字大约 2 分钟
布欧-Lewyon
2026-05-15
自定义 Hook 是 React 最强大的复用逻辑方式——将组件逻辑抽取为可复用的函数。
useLocalStorage
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 {
return initialValue
}
})
const setValue = (value: T | ((val: T) => T)) => {
const valueToStore = value instanceof Function ? value(storedValue) : value
setStoredValue(valueToStore)
window.localStorage.setItem(key, JSON.stringify(valueToStore))
}
return [storedValue, setValue] as const
}
// 使用
function App() {
const [theme, setTheme] = useLocalStorage('theme', 'light')
// ...
}useDebounce
function useDebounce<T>(value: T, delay: number = 500): T {
const [debouncedValue, setDebouncedValue] = useState(value)
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay)
return () => clearTimeout(timer)
}, [value, delay])
return debouncedValue
}
// 使用:搜索防抖
function Search() {
const [query, setQuery] = useState('')
const debouncedQuery = useDebounce(query, 500)
useEffect(() => {
if (debouncedQuery) fetchResults(debouncedQuery)
}, [debouncedQuery])
return <input value={query} onChange={e => setQuery(e.target.value)} />
}useFetch
interface FetchState<T> {
data: T | null
loading: boolean
error: Error | null
}
function useFetch<T>(url: string) {
const [state, setState] = useState<FetchState<T>>({
data: null,
loading: true,
error: null
})
useEffect(() => {
const controller = new AbortController()
setState(prev => ({ ...prev, loading: true }))
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(data => setState({ data, loading: false, error: null }))
.catch(error => {
if (error.name !== 'AbortError') {
setState({ data: null, loading: false, error })
}
})
return () => controller.abort()
}, [url])
return state
}useMediaQuery
function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState(
() => window.matchMedia(query).matches
)
useEffect(() => {
const media = window.matchMedia(query)
const listener = (e: MediaQueryListEvent) => setMatches(e.matches)
media.addEventListener('change', listener)
return () => media.removeEventListener('change', listener)
}, [query])
return matches
}
// 使用
function ResponsiveComponent() {
const isMobile = useMediaQuery('(max-width: 768px)')
return isMobile ? <MobileView /> : <DesktopView />
}Hook 规则
- 只在顶层调用 Hook:不在条件、循环、嵌套函数中调用。
- 只在 React 函数中调用:不要在普通 JS 函数中调用。
- 命名以
use开头:方便 lint 检测。
小结
- 自定义 Hook 以
use开头,内部可调用其他 Hook。 useLocalStorage、useDebounce、useFetch、useMediaQuery是高频通用 Hook。- 自定义 Hook 让逻辑复用不再依赖 HOC 或 Render Props。
- Hook 规则:顶层调用、只在函数组件/自定义 Hook 中调用。
上一节:useMemo / useCallback 与性能 下一节:闭包陷阱
