返回首页

react

分类:elysia
发布于:
阅读时间:15 分钟

这是一个非常棒的问题!你现在困惑的核心在于 “时间轴(Timeline)”

你不知道 React 的生命周期浏览器渲染Local Storage 读取API 请求 这四件事到底是谁先谁后。

不用担心,我们用一个最直观的 “去饭店点菜” 的例子,结合 React 的底层运行机制把这个过程画出来。


1. 错误的写法:为什么会报错?

(写法:初始值 null -> useEffect 读取)

想象你去一家饭店(页面加载),你是 API 请求,你口袋里的 VIP 卡currentDeptId

⏱️ 时间轴还原(毫秒级慢动作):

  1. T=0ms (代码解析)

    • JS 代码刚跑起来。

    • React 看到 useState(null)

    • 结论:此时内存里,VIP 卡(ID)是 “空”

  2. T=10ms (首次渲染 Render)

    • React 开始画页面。它问:“现在 ID 是多少?”

    • 内存回答:“是空。”

    • React 说:“行,那我就先按‘没卡’的情况画页面。”

  3. T=20ms (组件挂载 & 发起请求 - 💣 炸雷点)

    • 如果你用的是 react-query 或者 SWR 这种库,它们通常在组件一挂载(Mount)时就立即发起请求。

    • API 请求冲出去了! 带着 x-dept-id: null

    • 后端报错:“你没卡,滚出去!”(401/400 Error)。

  4. T=30ms (浏览器绘制 Paint)

    • 浏览器把刚才 React 画的“没卡”的界面展示给用户看。
  5. T=40ms (useEffect 终于执行了 - 🐢 迟到了)

    • React 规定:useEffect 必须等浏览器画完界面(T=30ms)之后才运行。

    • 这时候代码才慢悠悠地去 LocalStorage 翻口袋:“哎呀,我有 VIP 卡(ID=123)啊!”

    • 执行 setDeptId("123")

  6. T=50ms (二次渲染)

    • React 重新画页面,这次有了 ID。

    • 但是... 刚才那个报错的 API 请求已经无法撤回了! 用户已经看到了报错红框。


2. 正确的写法:为什么能修好?

(写法:初始值直接读取 LocalStorage)

我们把读卡的动作提前。

⏱️ 时间轴还原:

  1. T=0ms (代码解析 - ✅ 关键改变)

    • JS 代码刚跑起来。

    • React 看到:

      JavaScript

      useState(localStorage.getItem("deptId") || null)
      
    • JS 是同步执行的。在这一行代码结束前,程序会暂停,去翻 LocalStorage(虽然很快,但确实发生了)。

    • 拿到 ID "123"。

    • 结论:在 React 开始干活之前,内存里的初始值已经是 "123" 了!

  2. T=10ms (首次渲染 Render)

    • React 开始画页面。它问:“现在 ID 是多少?”

    • 内存回答:“是 123。”

    • React 说:“好,按‘有卡’的情况画页面。”

  3. T=20ms (组件挂载 & 发起请求)

    • react-query 发起请求。

    • API 请求冲出去了! 带着 x-dept-id: 123

    • 后端响应:“尊贵的 VIP,这是您的数据。”(200 OK)。

  4. T=30ms (浏览器绘制)

    • 用户直接看到有数据的页面。
  5. T=40ms (useEffect)

    • 这时候就算有 useEffect,也没事干了,因为 ID 早就有了。

3. 图解:DOM 和 React 到底在干嘛?

React 对于 DOM 的操作,遵循一个铁律:所见即所得(UI = f(State))

  • 红色区域 (Sync):初始化 State、计算 Virtual DOM。这是同步的,必须一口气干完。

  • 绿色区域 (DOM):React 把计算结果推给浏览器,浏览器把像素画在屏幕上。

  • 蓝色区域 (Async)useEffect。这是异步的。React 为了不让界面卡顿,特意把 useEffect 安排在 “浏览器画完之后” 才执行。

为什么 useEffect 会导致竞态?

因为 API 请求(通常由 useQuery 等库触发)往往紧跟在 渲染阶段 或 挂载阶段。

而 useEffect 里的 setState 必定发生在 渲染 -> 绘制 -> Effect 这一长串流程的最后。

等你 Effect 设置好 ID,黄花菜都凉了。

总结给 React 开发者

  1. 初始化 (useState/Zustand create)最先 执行的,比组件渲染还早。

  2. useEffect最后 执行的,比浏览器画图还晚。

  3. Local Storage 是同步 API (getItem 是同步方法),所以由于它很快,完全可以在初始化阶段直接读。

一句话口诀:

“不要等进了考场(useEffect)再翻书(读Storage),要在进考场前(初始化)就把知识(ID)装进脑子(State)里。”