Glittering's blog Glittering's blog
Home
  • 学习手册

    • 《JavaScript教程》
    • 《TypeScript教程》
    • 《Git》
    • 《Vite》
    • 《Vue3》
    • 《React18》
    • 《CSS》
    • 《Tailwind CSS》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)

Glitz Ma

前端开发工程师
Home
  • 学习手册

    • 《JavaScript教程》
    • 《TypeScript教程》
    • 《Git》
    • 《Vite》
    • 《Vue3》
    • 《React18》
    • 《CSS》
    • 《Tailwind CSS》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)
  • react基础

  • react原理

    • Vue3 effect vs react hooks
    • Fiber是什么
    • renderWithHooks是怎么挂到Fiber
    • mount/update为什么能切换
    • React 更新系统的“回路闭合点”
    • 为什么hooks不能写在条件里
    • 为啥hooks要用全局变量(而不是参数传递)
      • 一、Hooks 到底用到了哪些“全局变量”?
      • 二、为什么不能用参数传递?(你一定会想到)
        • 看起来是不是很干净?
      • 三、参数传递的第一个致命问题:API 被污染
      • 四、第二个致命问题:render 无法“透明重跑”
      • 五、第三个致命问题:无法做到“零编译”
        • 1️⃣ 编译期注入
      • 六、全局变量的“隐藏优势”:中断友好
        • 中断时会发生什么?
      • 七、为什么“全局”却是安全的?
        • 并不是“真正的全局状态”
      • 八、Vue effect 对照(帮你对齐)
      • 九、如果强行不用全局变量,会怎样?
      • 十、终极一句话总结(这条是设计哲学)
  • react全家桶

  • 《React18》学习笔记
  • react原理
mamingjuan
2025-09-25
目录

为啥hooks要用全局变量(而不是参数传递)

这个问题已经不是“Hooks 怎么实现”,而是 React 为什么只能这样设计。 我们从 “如果不用全局变量会怎样” 反推,你会发现这是一个几乎被逼出来的选择。

先给结论: Hooks 使用全局变量,不是偷懒,而是为了保证: render 可重跑、可中断、零侵入、零编译成本。


# 一、Hooks 到底用到了哪些“全局变量”?

核心只有这几个:

let currentlyRenderingFiber = null
let workInProgressHook = null
let currentHook = null
1
2
3

再加一个:

ReactCurrentDispatcher.current
1

👉 所有 Hook 都通过这些“隐式上下文”工作


# 二、为什么不能用参数传递?(你一定会想到)

理想中的 API 可能是:

function useState(ctx, initial) { ... }

function App(props, ctx) {
  const [a, setA] = useState(ctx, 0)
}
1
2
3
4
5

或者更“隐式”一点:

function App(props, ctx = useHooksContext()) { ... }
1

# 看起来是不是很干净?


# 三、参数传递的第一个致命问题:API 被污染

React 的核心目标之一是:

组件函数 = 普通 JS 函数

Component(props) → JSX
1

如果引入参数:

  • 每个组件都要接收 ctx
  • 每个 Hook 都要显式传
  • 用户心智复杂度暴涨

👉 这直接违背 React 的设计哲学


# 四、第二个致命问题:render 无法“透明重跑”

Fiber 的核心前提是:

render 是可丢弃、可重试的纯函数

如果 Hook 上下文靠参数:

render(Component, ctx)
1

你必须保证:

  • ctx 在多次 render 间一致
  • ctx 生命周期与 Fiber 同步
  • ctx 在中断 / 恢复时可追溯

👉 你实际上是在“手写 Fiber”


# 五、第三个致命问题:无法做到“零编译”

如果不用全局变量,剩下的方案只有:

# 1️⃣ 编译期注入

useState() 
↓ 编译
useState(__hookCtx, ...)
1
2
3

这意味着:

  • 必须有编译器
  • 必须锁定 DSL
  • 破坏 JS 直写

👉 React 明确拒绝这条路


# 六、全局变量的“隐藏优势”:中断友好

你前面已经理解 Fiber 的执行模型:

renderWithHooks
  set currentlyRenderingFiber
  execute Component()
  clear globals
1
2
3
4

# 中断时会发生什么?

  • render 被丢弃
  • 全局变量直接 reset
  • 不会留下半成品状态

如果是参数:

  • 你要保存 ctx
  • 要回滚
  • 要重放

👉 复杂度指数级上升


# 七、为什么“全局”却是安全的?

这是一个很容易被误解的点。

# 并不是“真正的全局状态”

而是:

render 期间的“动态作用域”

renderWithHooks() {
  set globals
  Component()
  clear globals
}
1
2
3
4
5
  • 单线程
  • 同一时间只 render 一个 Fiber
  • 没有并发写冲突

👉 逻辑上是安全的


# 八、Vue effect 对照(帮你对齐)

Vue3 React
activeEffect currentlyRenderingFiber
effect(fn) renderWithHooks
track 隐式 Hooks 隐式
编译增强 运行时约定

👉 两者都是 “隐式上下文”,只是位置不同。


# 九、如果强行不用全局变量,会怎样?

你会得到一个框架:

  • 组件签名被污染
  • Hook API 复杂
  • render 不再是纯函数
  • 中断 / 并发成本爆炸
  • 必须引入编译器

👉 这已经不是 React 了


# 十、终极一句话总结(这条是设计哲学)

Hooks 用全局变量,是为了用“最小运行时成本”, 换取“最大调度自由度”和“最干净的用户 API”。

这不是实现细节,是 核心设计选择。

上次更新: 2026/01/08, 10:00:37
为什么hooks不能写在条件里
react全家桶概览

← 为什么hooks不能写在条件里 react全家桶概览→

Copyright © 2015-2026 Glitz Ma
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式