Skip to content

React Hooks 要答到什么程度? ​

Hooks 让函数组件拥有状态和副作用。React 靠调用顺序把每一次的 Hook 对上一次渲染的状态,所以 Hook 只能写在组件或自定义 Hook 的顶层。useState 负责状态,useEffect 负责提交之后的副作用。

原理 ​

一次渲染里的状态更新会被收集起来,React 18 起在事件、定时器、Promise 里都会批处理。在这次函数执行过程中读到的仍是旧状态。要基于旧值计算,把函数传给 setState:

jsx
setCount((count) => count + 1)

useEffect(fn, deps) 在浏览器把这次更新画出来之后运行。deps 里的值变了,先跑上一次的清理函数,再跑这次的 fn。组件卸载时也会清理。

jsx
useEffect(() => {
  const controller = new AbortController()
  fetch(url, { signal: controller.signal })
  return () => controller.abort()
}, [url])

依赖数组记下的是这一次渲染的闭包。漏写依赖,效果函数会一直看到旧的 props 或 state。

useLayoutEffect 在绘制前同步运行,适合读取布局并立刻改 DOM。里面做重活会卡住首帧。

useMemo 缓存计算结果,useCallback 缓存函数,两者都是「依赖不变就返回上次的引用」。传给 memo 子组件、或者依赖本身很贵时才有意义。React Compiler 能在构建期自动做一部分这种缓存,面试仍然要能解释为什么引用变化会让子组件重新渲染。

useRef 是一个可变盒子。改 ref.current 不会触发渲染。它适合存定时器、DOM 节点、以及「变了但不需要画出来」的值。

React 19 里,ref 可以作为普通 prop 传入函数组件,多数场景不必再包 forwardRef。

自定义 Hook 只是名字以 use 开头、内部调用了 Hook 的函数。它复用的是状态逻辑,不复用状态本身。每个调用点都有自己的状态。

追问 ​

为什么不能写在条件里?

React 用链表按顺序记住 Hook。某一次少调用一个,后面的 Hook 会读到别人的状态。条件逻辑放进 Hook 里面,不要放在 Hook 的调用外面。

useEffect 空依赖和没有依赖有什么差别?

空数组:挂载后执行一次,卸载时清理。不写数组:每次提交后都执行。写了具体依赖:这些值用 Object.is 比出变化才执行。

状态没变,组件为什么还是渲染了?

父组件渲染,子组件默认跟着渲染。memo 只能挡住 props 的浅比较。context 值变了,所有读这份 context 的组件都会渲染。见 状态管理。

易错点 ​

  • setState 不是「异步函数」,但更新是调度出去的,当前函数里读不到新值。
  • useEffect 里用到的外部变量都应该出现在依赖里,再用清理函数处理过期请求,而不是故意漏依赖。
  • 每次渲染都新建对象再放进依赖,效果会每次都跑。

相关 ​