React Hooks
React Hooks 的整体分工、使用边界与常见模式总览。
[!info] related notes
- 所属 MOC: React MOC
- 上位主题: React
- 重点主题: useEffect, react-use-layout-effect, useState, useRef, useContext, react-use-reducer, useMemo, useCallback, react-memo, react-custom-hooks
- 前置知识: jsx, JavaScript
- 关系笔记: React Class Component vs Function Component
- 学习方法: 如何学习前端底层原理
React Hooks
Hooks 让函数组件拥有状态、上下文、副作用、引用和值缓存等能力。它们不是一组零散 API,而是 React 组件能力的统一入口。
Hooks 也是 React 对组件逻辑组织方式的一次升级:它让函数组件拥有“状态、生命周期、副作用、上下文、性能缓存、可复用逻辑”这些能力。
为什么 Hooks 重要
- 让函数组件取代大多数类组件场景
- 让状态逻辑和副作用逻辑可以抽成自定义 Hook 复用
- 让组件更容易按功能组织,而不是按生命周期切碎
Hooks 到底解决了什么
在 Hooks 之前:
- 同一件事常被拆到
componentDidMount、componentDidUpdate、componentWillUnmount - 复用逻辑经常要靠 HOC 或 render props
- 函数组件更轻,但能力不完整
Hooks 把“状态 + 副作用 + 逻辑组合”带回了函数组件,让你可以按功能聚合逻辑,而不是被生命周期 API 牵着走。
Hooks 的底层心智模型
组件每次渲染时,函数都会重新执行。React 之所以还能知道“哪个 state 属于哪个 Hook”,依赖的不是变量名,而是 Hook 的调用顺序。
可以把它先粗略理解成:
- 第 1 个 Hook 槽位
- 第 2 个 Hook 槽位
- 第 3 个 Hook 槽位
所以这段代码里:
function Counter() {
const [count, setCount] = useState(0)
const [name, setName] = useState('A')
}
React 真正依赖的是:
- 第 1 个
useState对应count - 第 2 个
useState对应name
可以把 Hooks 分成四组理解
状态类
useStateuseReducer
它们负责让组件记住会影响渲染的值。
进一步看:useState, react-use-reducer
副作用类
useEffectuseLayoutEffect
它们负责和 React 外部系统同步,而不是单纯“渲染后顺手做点事”。
进一步看:useEffect, react-use-layout-effect
引用与上下文类
useRefuseContext
它们分别解决“不参与渲染的可变值”和“跨层共享值”的问题。
进一步看:useRef, useContext
性能与体验类
useMemouseCallbackuseTransitionuseDeferredValue
它们主要用于优化重计算、引用稳定性和交互优先级。
进一步看:useMemo, useCallback
最重要的两条规则
- 只在 React 函数组件或自定义 Hook 中调用 Hook
- 只在顶层调用,不要放进条件、循环或嵌套函数里
原因不是风格偏好,而是 React 依赖 Hook 的调用顺序来匹配状态槽位。
错误例子:
function Bad({ visible }: { visible: boolean }) {
if (visible) {
useEffect(() => {
console.log('shown')
}, [])
}
const [count, setCount] = useState(0)
return <button onClick={() => setCount(count + 1)}>{count}</button>
}
当 visible 一会儿是 true,一会儿是 false 时,Hook 顺序就乱了。
为什么名字都要以 use 开头
这不只是命名风格。
- 它告诉团队和读代码的人:这是 Hook,不是普通工具函数
- 它让 ESLint 和工具链知道:这里要遵守 Hook 规则
- 它意味着这个函数内部可能继续调用其他 Hook
自定义 Hook 的价值
自定义 Hook 复用的是“状态 + 副作用 + 处理逻辑”,而不是直接复用 UI 结构。
常见例子有:useFetch、useToggle、useDebounce、usePrevious。
进一步看:react-custom-hooks
Hooks 学习里真正难的点
真正难的不是记住 API 名字,而是搞懂:
- 为什么 Hook 只能顶层调用
- 为什么每次渲染都会形成新闭包
- 为什么
useEffect不是“渲染后随便写逻辑” - 为什么依赖数组必须真实反映依赖
- 为什么
useMemo/useCallback不是默认必加
一套选择 Hook 的思维框架
- 需要让 UI 记住某个值:useState / react-use-reducer
- 需要和外部系统同步:useEffect / react-use-layout-effect
- 只想保存一个不触发渲染的值:useRef
- 需要跨层传值:useContext
- 有昂贵计算或需要稳定对象引用:useMemo
- 需要稳定函数引用:useCallback
- 想复用一段“状态 + 副作用 + 行为”:react-custom-hooks
最容易误用的 Hook
最容易被滥用的是 useEffect。很多本该写在渲染逻辑或事件处理里的代码,被误塞进了副作用里。要单独理解时,优先看 useEffect。
最短记忆方式
- Hooks 不是“函数组件的补丁”
- Hooks 是函数组件获取 React 能力的统一接口
- 真正会用 Hooks 的标志,是能区分状态、引用、副作用和性能优化各自的边界
学习检查
不看正文回答:
- Hooks 解决的核心问题为什么不只是“让函数组件有 state”?
- React 为什么要求 Hook 调用顺序稳定?
- state、effect、ref、context、memoization 分别解决哪一类问题?
- 为什么自定义 Hook 复用的是状态与行为逻辑,而不是 UI 结构?
- 为什么
useEffect是最容易被滥用的 Hook? - 一个值只需要跨 render 保留但不驱动 UI 时,为什么更接近 ref 而不是 state?
- 一个昂贵计算是否应该立刻加
useMemo?你还需要哪些证据? - 给一个新组件需求,能否先按职责选 Hook,再写 API?