React 常用 Hooks:用法 + 场景总结
React Hooks 是函数组件中管理“状态、生命周期、性能优化、组件通信”等能力的核心工具。
1. useState —— 状态管理(最常用)
用于:保存组件状态
基本用法
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
</div>
);
}
核心理解
const [状态, 修改状态的方法] = useState(初始值)
特点
- 修改状态会重新渲染组件
- 状态更新是异步的
- 不要直接修改状态
错误:
count = count + 1
正确:
setCount(count + 1)
场景
1)计数器
const [count, setCount] = useState(0)
2)表单输入
const [username, setUsername] = useState('');
<input
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
3)弹窗开关
const [visible, setVisible] = useState(false)
2. useEffect —— 副作用处理
用于:
处理“组件渲染之外”的事情
比如:
- 请求接口
- 定时器
- 事件监听
- DOM 操作
- 数据同步
生命周期理解
可以把它理解为:
componentDidMount
componentDidUpdate
componentWillUnmount
的组合版。
基本用法
useEffect(() => {
console.log('组件渲染了');
});
默认:
- 首次执行
- 更新也执行
依赖数组
1)不写依赖
useEffect(() => {
console.log('每次渲染都会执行');
});
场景:
- 调试
- 每次更新都同步
2)空数组 []
useEffect(() => {
console.log('只执行一次');
}, []);
等价于:
componentDidMount
场景:
- 页面初始化请求
- 初始化图表
- 初始化 websocket
3)有依赖
useEffect(() => {
console.log('count变化了');
}, [count]);
只有:
count 改变
才执行。
清理函数(重点)
useEffect(() => {
const timer = setInterval(() => {
console.log('执行中');
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
return 的函数:
组件卸载时执行
用于:
- 清除定时器
- 移除事件
- 取消订阅
- 关闭 websocket
场景
请求接口
useEffect(() => {
fetchData();
}, []);
监听窗口变化
useEffect(() => {
const resize = () => console.log(window.innerWidth);
window.addEventListener('resize', resize);
return () => {
window.removeEventListener('resize', resize);
};
}, []);
3. useRef —— 保存引用
用途:
- 获取 DOM
- 保存变量(不会重新渲染)
获取 DOM
import { useRef } from 'react';
function App() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} />
<button onClick={focusInput}>
聚焦
</button>
</>
);
}
保存变量(不会触发更新)
const countRef = useRef(0);
countRef.current++;
特点:
- 修改不会重新渲染
- 类似实例变量
useRef vs useState
| 对比 | useState | useRef |
|---|---|---|
| 修改后重新渲染 | 会 | 不会 |
| 适合 UI 数据 | 是 | 否 |
| 保存临时变量 | 不推荐 | 推荐 |
场景
防抖节流 timer
const timerRef = useRef(null)
保存上一次值
const prevValue = useRef();
操作 DOM
inputRef.current.focus()
4. useMemo —— 缓存计算结果
用于:
避免重复计算
基本用法
const total = useMemo(() => {
return list.reduce((a, b) => a + b, 0);
}, [list]);
只有:
list 变化
才重新计算。
为什么需要?
例如:
大量数据计算
每次 render 都重新执行会卡。
useMemo 可以缓存结果。
场景
大数据计算
const expensiveValue = useMemo(() => {
return heavyCompute(data);
}, [data]);
避免子组件重复渲染
const userInfo = useMemo(() => {
return {
name,
age
};
}, [name, age]);
否则:
{}
每次 render 都是新对象。
5. useCallback —— 缓存函数
用于:
缓存函数
基本用法
const handleClick = useCallback(() => {
console.log('点击');
}, []);
为什么需要?
React 中:
函数每次 render 都会重新创建
可能导致:
子组件重复渲染
配合 memo 使用(重点)
父组件:
const handleAdd = useCallback(() => {
console.log('add');
}, []);
<Child onAdd={handleAdd} />
子组件:
export default memo(Child)
这样:
父组件更新
不会导致子组件重新渲染
useMemo vs useCallback
| Hook | 缓存什么 |
|---|---|
| useMemo | 值 |
| useCallback | 函数 |
场景
传递函数给子组件
onClick
onChange
submit
防止重复渲染
特别是:
- React.memo
- 大列表
- 复杂组件
6. useContext —— 跨组件通信
解决:
props 一层层传递
创建 Context
import { createContext } from 'react';
const ThemeContext = createContext();
Provider 提供数据
<ThemeContext.Provider value="dark">
<App />
</ThemeContext.Provider>
子组件使用
import { useContext } from 'react';
const theme = useContext(ThemeContext);
场景
全局主题
dark/light
用户信息
登录用户
权限管理(RBAC)
比如:
role = admin
整个系统共享。
7. useReducer —— 复杂状态管理
适用于:
复杂 state 逻辑
类似 Redux。
基本用法
import { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'add':
return {
count: state.count + 1
};
case 'minus':
return {
count: state.count - 1
};
default:
return state;
}
}
function App() {
const [state, dispatch] = useReducer(reducer, {
count: 0
});
return (
<>
<p>{state.count}</p>
<button onClick={() =>
dispatch({ type: 'add' })
}>
+1
</button>
</>
);
}
核心理解
dispatch(action)
→ reducer处理
→ 返回新state
场景
表单复杂状态
多个输入框
多状态联动
loading
data
error
替代 Redux 小型场景
中小项目状态管理
Hooks 之间关系图
状态管理:
useState
useReducer
副作用:
useEffect
DOM/变量:
useRef
性能优化:
useMemo
useCallback
跨组件通信:
useContext
企业开发里最常用组合
页面请求
useState + useEffect
表单
useState/useReducer
权限系统(RBAC)
useContext
表格性能优化
useMemo + useCallback
Echarts 图表
(你之前做性能平台会很常见)
useRef + useEffect
初始化:
echarts.init(ref.current)
面试高频问题
1)为什么 useEffect 会重复执行?
因为:
组件重新 render
导致 effect 重新触发。
2)useMemo 一定能优化性能吗?
不一定。
因为:
缓存本身也有成本
小计算没必要。
3)为什么 useRef 不会触发更新?
因为:
它不参与 React 渲染
只是保存对象。
4)什么时候用 useReducer?
当:
state逻辑复杂
多个状态关联
时优于 useState。
一张总结表
| Hook | 作用 | 典型场景 |
|---|---|---|
| useState | 状态管理 | 表单、开关、计数 |
| useEffect | 副作用 | 请求接口、监听 |
| useRef | DOM/缓存变量 | input聚焦、timer |
| useMemo | 缓存值 | 大计算 |
| useCallback | 缓存函数 | 子组件优化 |
| useContext | 跨组件通信 | 用户、主题、权限 |
| useReducer | 复杂状态 | 表单、Redux思想 |