Skip to content

一、Hooks 设计初衷(为什么会有 Hooks)

React 16.8 之前:

  • 逻辑复用 → HOC / render props
  • 状态管理 → class component
  • 生命周期 → 分散在 componentDidMount / update / unmount
  • this 指向复杂
  • 逻辑分散难维护

Hooks 的目标:

  • ✅ 逻辑复用
  • ✅ 函数组件拥有状态
  • ✅ 副作用逻辑可组合
  • ✅ 更好的 tree shaking

二、核心 Hooks 总览

1️⃣ useState

js
const [state, setState] = useState(initialState)

关键点

  • setState 是异步批处理(React 18 自动批处理)
  • 更新可能被合并
  • 支持函数式更新
js
setCount(prev => prev + 1)

面试深挖

  • 为什么不能在条件里写 useState?

    • Hooks 调用顺序必须稳定(依赖 fiber 的 hooks 链表)

2️⃣ useEffect

副作用处理核心 API。

js
useEffect(() => {
  // effect
  return () => {
    // cleanup
  }
}, [deps])

执行时机

  • 首次渲染后执行
  • 依赖变化后执行
  • 卸载执行 cleanup

React 18 重要变化

  • 严格模式下会执行两次(开发环境)
  • 用于检测副作用安全性

useEffect vs useLayoutEffect

useEffectuseLayoutEffect
执行时机DOM 渲染后DOM 渲染前
是否阻塞渲染不阻塞阻塞
场景请求/订阅DOM 读写

3️⃣ useRef

js
const ref = useRef(null)

特点:

  • 不触发重渲染
  • 保存可变值
  • 可获取 DOM

高级用法:

js
const latestFn = useRef(fn)

解决闭包陷阱问题。

4️⃣ useMemo

js
const memoValue = useMemo(() => compute(), [deps])
  • 缓存计算结果
  • 防止重复计算
  • 用于对象引用稳定

⚠️ 滥用会增加内存压力

5️⃣ useCallback

js
const memoFn = useCallback(() => {}, [deps])

本质:

js
useMemo(() => fn, deps)

用途:

  • 子组件 React.memo 优化
  • 事件函数引用稳定

6️⃣ useContext

js
const value = useContext(Context)

问题:

  • 任意 value 变化 → 所有 consumer 重新渲染

解决:

  • 拆分 context
  • useContextSelector
  • 外部状态库

7️⃣ useReducer

js
const [state, dispatch] = useReducer(reducer, initialState)

适合:

  • 复杂状态
  • 多字段联动
  • 表单管理

类比:

  • Redux 精简版

三、React 18 新 Hooks

useTransition

解决卡顿问题。

js
const [isPending, startTransition] = useTransition()
  • 标记低优先级更新
  • 不阻塞 UI

适合:

  • 搜索过滤
  • 大列表渲染

useDeferredValue

js
const deferred = useDeferredValue(value)

延迟更新某个值。

useId

解决 SSR hydration id 不一致问题。

四、Hooks 底层原理

React 内部:

  • 每个 Fiber 有一个 hooks 单向链表
  • 每个 hook 结构:
js
{
  memoizedState,
  queue,
  next
}

调用顺序决定定位:

js
useState()
useEffect()
useState()

必须顺序一致。

这就是为什么:

❌ 不能在 if 里调用 hooks

五、闭包陷阱问题(高频)

js
useEffect(() => {
  setInterval(() => {
    console.log(count)
  }, 1000)
}, [])

count 永远是初始值。

解决方案:

  1. 依赖数组加 count
  2. useRef 保存最新值
  3. 使用函数式更新

六、自定义 Hooks

本质

函数 + 内部调用其他 hooks

例:

js
function useRequest(url) {
  const [data, setData] = useState(null)

  useEffect(() => {
    fetch(url).then(r => r.json()).then(setData)
  }, [url])

  return data
}

优势:

  • 逻辑复用
  • 关注点分离
  • 组合式编程

七、性能优化思路

1️⃣ 减少无意义重渲染

  • React.memo
  • useMemo
  • useCallback
  • 拆分组件

2️⃣ 状态粒度控制

  • 局部状态
  • 避免顶层大对象

3️⃣ 长列表

  • 虚拟列表
  • useTransition
  • 分块渲染

八、和 Vue3 Composition API 对比

React HooksVue3
useStateref/reactive
useEffectwatchEffect
自定义 hookcomposable
useMemocomputed
fiber 调度响应式依赖追踪

最大区别:

  • React 依赖调用顺序
  • Vue 依赖 Proxy 依赖收集

十、最终总结

Hooks 是 React 在函数式组件中实现状态和副作用管理的机制,本质是基于 Fiber 链表按调用顺序存储状态,通过闭包和调度系统实现响应式更新。