
先问一句:这个 Hook 值得引一个依赖吗
一个 useLocalStorage 大概二十行,一个 useWindowSize 十行以内。这类需求自己写更快,也少一个要跟版本的依赖。
值得引库的是三种情况:逻辑本身复杂(表单校验、虚拟列表、请求缓存);涉及大量浏览器 API 的边界(剪贴板、媒体查询、各类传感器);你需要 SSR 与水合的安全性,而这些坑别人已经踩过。
选的时候看四件事:仓库最近的提交时间、TypeScript 类型是否完整、SSR 相关 issue 怎么处理、以及能不能按需引入。
通用 Hook
ahooks
60 多个 Hook,按请求、存储、DOM、传感器等分类,中文文档完善。仓库 https://github.com/alibaba/hooks。
它的 useRequest 把加载态、缓存、轮询、取消都包了:
import { useRequest } from 'ahooks'
function User() { const { data, loading, error } = useRequest(() => fetch('/api/user').then(res => res.json()) )
if (loading) return <div>加载中……</div> if (error) return <div>错误:{error.message}</div> return <div>用户名:{data?.name}</div>}react-use
覆盖面最广的一类,按 Browser、State、Element、Effect、Sensor 等分类,浏览器 API 的封装很多。仓库 https://github.com/streamich/react-use。
import { useLocalStorage } from 'react-use'
function App() { const [theme, setTheme] = useLocalStorage('theme', 'light')
return ( <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}> 切换主题:{theme} </button> )}@react-hookz/web
类型更严的通用库,零依赖,带 useAsync、useLocalStorageValue 这类强类型封装。仓库 https://github.com/react-hookz/web。
import { useAsync } from '@react-hookz/web'
function Data() { const { result, error, status } = useAsync(async () => { const res = await fetch('/api/data') return res.json() }, [])
if (status === 'loading') return <div>加载中……</div> if (status === 'error') return <div>错误:{error.message}</div> return <div>{JSON.stringify(result)}</div>}表单
react-hook-form
核心是「非受控」,值靠 ref 收集,所以打字时不会整个表单重渲染。仓库 https://github.com/react-hook-form/react-hook-form。
import { useForm } from 'react-hook-form'
function Login() { const { register, handleSubmit, formState: { errors } } = useForm() const onSubmit = data => console.log(data)
return ( <form onSubmit={handleSubmit(onSubmit)}> <input {...register('email', { required: '邮箱必填' })} placeholder="邮箱" /> {errors.email && <p>{errors.email.message}</p>} <button type="submit">登录</button> </form> )}服务端数据
TanStack Query
管的是「服务端状态」这一层:缓存、失效、重试、后台刷新、分页与无限滚动。仓库 https://github.com/TanStack/query。
这里要盯住版本。旧写法里的 loading 在 v5 已经不在返回值里了:
import { useQuery } from '@tanstack/react-query'
function Posts() { const { data, isPending, error } = useQuery({ queryKey: ['posts'], queryFn: () => fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json()), })
if (isPending) return <div>加载中……</div> if (error) return <div>错误:{error.message}</div> return <ul>{data.map(post => <li key={post.id}>{post.title}</li>)}</ul>}v5 把状态标志拆成了几个:isPending 表示还没有数据,isFetching 表示后台正在刷新;error 是对象,不是字符串。这几个的准确语义以官方迁移指南为准,抄示例之前先对一遍版本。
几条提醒
同一个用途别同时引两个库。两个都管请求缓存时,失效时机很难对齐,排查起来很费时间。
库提供的 Hook 依然受 Hook 规则约束:只能在组件或自定义 Hook 的顶层调用,别放进条件或循环。
Star 数与维护状态都会变,本文不写「首选」「之王」这类结论。选之前看一眼仓库最近的提交和 issue 的处理情况。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 190 天
部分内容可能已过时