React 常用 Hooks
前置:React 列表与表单
目标:掌握
useEffect/useMemo,并会抽自定义 Hook。
引言
useState 管组件里的数据;一碰到「改标题、开定时器、订消息」,就要 useEffect 。贵计算用 useMemo ;多处重复的逻辑,抽成 自定义 Hook。
本篇 demo:搜索词同步到 document.title、每秒 ticks++、useMemo 算 doubled。
动手
完整代码:react2/05-hooks/demo
bash
cd react2/05-hooks/demo
npm install && npm run dev
输入框打字看浏览器标签页标题变化;下面计时会每秒跳一次。
1. useState:组件内状态
tsx
const [query, setQuery] = useState('')
const [ticks, setTicks] = useState(0)
- 第一个值是当前 state,第二个是更新函数。
- 输入框受控:
value={query},onChange里setQuery。 - 计时不要写
setTicks(ticks + 1)连点踩旧值;用函数式:setTicks((t) => t + 1)。
2. useEffect:同步到外部系统
React 渲染只负责 UI。改 document.title、setInterval、订阅,都属于「外部系统」,放进 useEffect。
定时器(挂载一次)
tsx
useEffect(() => {
const id = window.setInterval(() => setTicks((t) => t + 1), 1000)
return () => window.clearInterval(id) // 卸载时清理
}, [])
- 依赖数组
[]:只在挂载时跑,卸载时执行清理。 - 忘了
clearInterval→ 切页后定时器还在跑,可能报错或泄漏。
依赖变了再跑
自定义 Hook 里按 title 更新文档标题:
tsx
useEffect(() => {
const prev = document.title
document.title = title
return () => {
document.title = prev // 还原,避免脏标题残留
}
}, [title])
title 变 → 先跑上一次的 cleanup,再跑新的 effect。
怎么选依赖
| 依赖 | 含义 |
|---|---|
[] |
只挂载 / 卸载各一次 |
[a, b] |
a 或 b 变就重跑 |
| 省略 | 每次渲染后都跑(几乎总是错的,别省略) |
3. useMemo:缓存计算结果
tsx
const doubled = useMemo(() => ticks * 2, [ticks])
只有 ticks 变才重算。本 demo 里 ticks * 2 很便宜,是为了看清写法。
什么时候值得用:
- 计算明显贵(大列表过滤、复杂派生数据)
- 要把「引用稳定」的值传给下游(配合
memo/ 依赖数组)
什么时候别用: 简单加减、拼字符串------多包一层反而难读,优化收益为零。
4. 自定义 Hook:复用带状态的逻辑
约定:名字以 use 开头,内部可以调其它 Hooks。
tsx
function useDocumentTitle(title: string) {
useEffect(() => {
const prev = document.title
document.title = title
return () => {
document.title = prev
}
}, [title])
}
// 组件里一行搞定
useDocumentTitle(query ? `搜索:${query}` : 'Hooks 示例')
好处:标题逻辑和 UI 解耦;别的页面也能 useDocumentTitle('设置')。
对照表
| Hook / 模式 | 干什么 | demo 里的例子 |
|---|---|---|
useState |
组件状态 | query、ticks |
useEffect |
同步外部系统 + 清理 | 定时器、document.title |
useMemo |
按依赖缓存计算结果 | doubled = ticks * 2 |
| 自定义 Hook | 打包可复用逻辑 | useDocumentTitle |
常见坑
| 做法 | 问题 |
|---|---|
| effect 里改 state 却漏依赖 | 闭包拿到旧值;或 eslint 警告被忽略后行为诡异 |
| 订阅 / 定时器不 cleanup | 内存泄漏、卸载后仍 setState |
到处 useMemo / useCallback |
代码变吵,性能没变好 |
自定义 Hook 不以 use 开头 |
没法静态检查 Hooks 规则 |
demo 在练什么
App.tsx 三件事叠在一起:
- 搜索框 →
useDocumentTitle改标签页标题 useEffect+setInterval→ticks每秒 +1,卸载清理useMemo→ 展示doubled
自己改一改:把 useMemo 换成「输入长度 > 3 才显示 doubled」,或给 useDocumentTitle 加前缀 [App]。
运行截图
