闭包是自定义hook实现状态隔离的根本原因,它使每次调用生成独立作用域,确保各组件间状态互不干扰、跨渲染值不丢失,并支撑依赖快照语义与私有上下文封装。

闭包是自定义 Hook 能“各管各的状态、互不干扰”的根本原因——它不是 React 加的限制,而是 JavaScript 语言天然提供的能力,React 只是巧妙地依赖并约束了它。
每次调用都生成独立的作用域
函数组件每次渲染都会重新执行,useCounter() 这样的自定义 Hook 也被反复调用。但因为它是普通函数,内部声明的 const [count, setCount] = useState(0) 和 increment 函数之间形成闭包关系:increment 始终能访问到本次调用所创建的 count 变量,而不是上一次或别的组件里的 count。
- 同一组件内多次调用 useCounter(),得到的是两套完全隔离的 count 和 setCount
- 两个不同组件分别调用 useCounter(),彼此状态零共享、零污染
- 这种隔离不靠命名区分,也不靠 React “识别”,纯粹由 JavaScript 作用域链和闭包机制保障
让状态和逻辑在重渲染中“不丢失”
组件更新时函数体重跑,局部变量本该被销毁。但只要某个变量被返回的函数(比如事件处理器、定时器回调)持续引用,JavaScript 引擎就会通过闭包把它保留在内存中。
- usePrevious 实现中保存的 prevRef.current,就是靠闭包维持跨渲染的值
- useInterval 内部的 timer ID 和回调函数,也依赖闭包绑定当前周期的参数与状态
- React 不会主动清理这些闭包,只要组件还挂载着,闭包链就一直有效
支撑依赖数组的“快照语义”
useEffect、useMemo 的依赖项不是实时读取的,而是在闭包创建那一刻被捕获的“快照”。自定义 Hook 封装这类逻辑时,闭包决定了副作用看到的是哪一帧的数据。
- 如果在 useEffect 里直接使用外部传入的 value,它就是定义时的值,不会随后续渲染自动更新
- 想读最新值,得配合 useRef + useEffect 更新 ref.current,再在闭包中读 ref —— 这是平衡“稳定性”和“新鲜度”的常用模式
- 闭包失效(比如把状态挂到 window 上)会导致 stale closure,表现为点击后读到旧 count
封装私有上下文,暴露干净接口
一个复杂的 useForm({ initialValues }),内部可能组合 useState、useEffect、useCallback 等多个 Hook。闭包把这些逻辑“锁”在一个私有环境中:
- 所有校验函数、reset 方法、submit 处理器,都闭包引用同一份 formState
- 用户只能通过 return 出来的 { values, errors, submit } 交互,无法绕过接口直接篡改 state
- 这就是为什么自定义 Hook 是轻量级模块化单元——闭包 + 函数返回 = 隐私边界











