标准答案

  1. 函数组件中的 state 是当前这次 render 的快照,回调函数会捕获创建它时那次 render 的变量。
  2. 旧 state 常见于 setTimeout、setInterval、Promise 回调、事件监听、订阅回调和漏依赖的 useEffect。
  3. 如果下一次状态依赖上一次状态,优先使用函数式更新,例如 setCount(prev => prev + 1)。
  4. 如果回调需要读取最新值,但不希望触发重新渲染,可以用 useRef 保存最新值。
  5. 如果 effect 依赖某个 state 或 prop,应把它写进依赖数组,或者调整逻辑避免在 effect 中读取过期闭包。

题目解析

这道题的核心不是“闭包有问题”,而是 React 的每次渲染都会产生一组新的变量快照。

函数式更新解决的是“根据上一次 state 计算下一次 state”;ref 解决的是“异步回调里读取最新可变值”。两者用途不同。

不要为了消除依赖数组 warning 就删依赖。很多旧 state 问题就是从错误压制 hooks 依赖开始的。

代码示例

下面的定时器用函数式更新避免捕获旧 count。

TSX
function Counter() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const timer = window.setInterval(() => {
      setCount(prev => prev + 1)
    }, 1000)

    return () => window.clearInterval(timer)
  }, [])

  return <span>{count}</span>
}

常见误区

  • 定时器里直接写 setCount(count + 1),导致一直基于初始 count 更新。
  • effect 使用了 state 或 prop,却为了只执行一次而故意留空依赖数组。
  • 把所有旧值问题都用 ref 解决,导致状态变化不触发渲染,UI 不更新。

作者信息