最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React處理高頻的實時數(shù)據(jù)的解決方案

 更新時間:2025年11月19日 08:24:09   作者:mCell  
本文介紹了在開發(fā)一個實時日志查看器時遇到的React錯誤Maximum update depth exceeded(超過最大更新深度),并分析了問題的根源,解決了這個問題,需要的朋友可以參考下

最近,我遇到了一個很有意思的 React 問題。

我需要開發(fā)一個實時的日志查看器,功能上需要實時展示服務(wù)運行的日志。因為這個項目是內(nèi)部的,我這里大概抽象一下:

后端使用 SSE(Server-Sent Events) 技術(shù),源源不斷地把日志推送給前端。

當(dāng)日志一條一條、不緊不慢地過來時,一切正常。

但是,當(dāng)我預(yù)覽一個已經(jīng)完成的任務(wù)日志時,網(wǎng)頁卡頓了一下。瀏覽器控制臺顯示了一個 React 開發(fā)者很熟悉的錯誤:

Uncaught Error: Maximum update depth exceeded... (錯誤:超過最大更新深度)

這個錯誤通常意味著,存在什么組件陷入了無限循環(huán)。比如,組件的渲染函數(shù)里直接調(diào)用了 setState,導(dǎo)致“渲染 → 更新狀態(tài) → 觸發(fā)渲染 → ...”的死循環(huán)。

比如這樣:

export default function Demo() {
  const [count, setCount] = useState(0)
  setCount(count + 1)
  return <h1>Count: {count}</h1>
}

但我的代碼并沒有這樣的邏輯,該使用 useEffect 的地方都使用了。我只是在 SSE 的事件回調(diào)里更新狀態(tài)。

// 示意代碼
const source = new EventSource("/api/logs")
source.addEventListener("log", (event) => {
  // 每來一條日志,就調(diào)用 set 函數(shù)
  appendLog(event.data)
})

那么,問題出在哪里呢?

問題的根源:高頻更新

起初我以為是哪里的更新邏輯不對,讓 claude 排查很久都沒找到具體問題。在給現(xiàn)有函數(shù)增加了不少緩存,比如useMemo,useCallback,甚至 React.memo 都使用上了,仍舊沒有解決這個報錯。

代碼沒有問題,那么問題就應(yīng)該出現(xiàn)在一些極端場景導(dǎo)致的高頻渲染。比如網(wǎng)絡(luò)?我才打開控制臺的網(wǎng)絡(luò)部分,看到幾乎在很短時間內(nèi),上百條的 log 被推送過來!

到這里問題就和清晰了:當(dāng)服務(wù)器在短時間內(nèi)(比如 1 秒內(nèi))推送上百條日志時,每一個 log 都觸發(fā)了 React 進行重新渲染,這里觸發(fā)了 React 的某些機制,React 對這種行為發(fā)出了報錯。

React 內(nèi)部有一個“嵌套更新計數(shù)器”,用來防止無限循環(huán)。

簡單說,如果在一次渲染(Render)的過程中,又因為某些原因觸發(fā)了新的狀態(tài)更新,這就叫“嵌套更新”。當(dāng)這個次數(shù)短時間內(nèi)超過一個閾值(通常是 50 次),React 就會認為你“可能”寫了一個 Bug,于是主動拋出錯誤,終止程序。

我們的問題就出在這里。SSE 的事件回調(diào)來得太快了。

當(dāng)服務(wù)器在 1 秒內(nèi)推送 150 條日志時,瀏覽器的事件循環(huán)會瘋狂執(zhí)行回調(diào):

  1. SSE 事件 1 抵達 → appendLog() → 觸發(fā) React 更新(第 1 次)
  2. React 還沒來得及渲染,SSE 事件 2 抵達 → appendLog() → 觸發(fā) React 更新(第 2 次)
  3. ...
  4. SSE 事件 50 抵達 → appendLog() → 觸發(fā) React 更新(第 50 次)
  5. SSE 事件 51 抵達 → appendLog() → 觸發(fā) React 更新(第 51 次)

在 React 看來,這 51 次更新幾乎是“同時”發(fā)生的,它無法分辨這是“51 條獨立日志”還是“一個死循環(huán)”。為了保護自己,它選擇了報錯。

問題的本質(zhì)是:數(shù)據(jù)接收的頻率(高頻)和 React 狀態(tài)更新的頻率(低頻)不匹配。

我們不能每收到一條數(shù)據(jù),就立刻更新一次狀態(tài)。

后續(xù)我了解到 React 18 版本對高頻渲染的問題進行了優(yōu)化,但它目前僅適用于 React 事件處理函數(shù)內(nèi)的同步更新。對于 SSE 回調(diào)、fetch 回調(diào)、setInterval 等異步事件源觸發(fā)的更新,仍需手動實現(xiàn)批處理。

解決方案:批處理(Batching)

既然不能一條一條地更新,那很自然就想到,能不能把日志“攢一下”,再一次性提交給 React?

這就是“批處理”(Batching)思想。

我們不再是“來一條,更新一次”,而是“來 N 條,更新一次”。

實現(xiàn)這個功能的關(guān)鍵,是需要一個“緩沖區(qū)”(Buffer)和一個“定時器”(Timer)。

  1. 緩沖區(qū):需要一個地方暫存日志,但這個地方本身不能是 React 的 state(否則又觸發(fā)渲染了)。useRef 是最合適的人選。
  2. 定時器:需要一個機制,在“攢”日志的間隙,把它們統(tǒng)一提交。setTimeout(..., 0) 是這里的法寶。

代碼實現(xiàn)

我們來改造一下 log 事件的處理。

首先,在組件里定義緩沖區(qū)和定時器:

export default function LogPage() {
  // 1. 從 store 獲取批量更新的方法
  const appendLogs = useLogStore((state) => state.appendLogs)

  // 2. 批處理緩沖區(qū)(使用 ref 不會觸發(fā)渲染)
  const batchBufferRef = useRef([])

  // 3. 定時器引用(保證只有一個定時器在運行)
  const batchTimerRef = useRef(null)

  // ...
}

其次,實現(xiàn)一個“提交緩沖區(qū)”的函數(shù) flushBatch

// 4. 批量提交函數(shù)
const flushBatch = useCallback(() => {
  // 如果緩沖區(qū)有數(shù)據(jù)
  if (batchBufferRef.current.length > 0) {
    // 一次性提交給 store
    appendLogs(batchBufferRef.current)
    // 清空緩沖區(qū)
    batchBufferRef.current = []
  }
  // 重置定時器引用
  batchTimerRef.current = null
}, [appendLogs]) // 依賴 appendLogs

最后,修改 SSE 的事件處理函數(shù) handleLogEvent

// 5. 新的 SSE 事件處理函數(shù)
const handleLogEvent = useCallback(
  (event) => {
    const entry = {
      /* ...解析日志... */
    }

    // 重點:不再直接調(diào)用 appendLog
    // 而是將日志加入緩沖區(qū)
    batchBufferRef.current.push(entry)

    // 如果還沒有計劃批處理,則在下一個事件循環(huán)中執(zhí)行
    if (batchTimerRef.current === null) {
      batchTimerRef.current = window.setTimeout(flushBatch, 0)
    }
  },
  [flushBatch] // 依賴 flushBatch
)

為什么是setTimeout(..., 0)?

你可能會問,為什么是 setTimeout(..., 0)?

這是一個很巧妙的技巧。它并不是真的“延遲 0 毫秒”,而是告訴瀏覽器:“請在當(dāng)前這一輪事件循環(huán)(Event Loop)的同步代碼都執(zhí)行完之后,再執(zhí)行這個 flushBatch 函數(shù)。”

當(dāng) 150 條日志在短時間內(nèi)涌入時,會發(fā)生什么?

  1. 事件 1 抵達 → push 到緩沖區(qū) → setTimeout 注冊一個 flushBatch 回調(diào)。
  2. 事件 2 抵達 → push 到緩沖區(qū) → 檢查定時器,發(fā)現(xiàn)已有,跳過。
  3. 事件 3 抵達 → push 到緩沖區(qū) → 跳過。
  4. ...
  5. 事件 150 抵達 → push 到緩沖區(qū) → 跳過。
  6. (當(dāng)前宏任務(wù)結(jié)束,所有同步代碼執(zhí)行完畢)
  7. 瀏覽器從任務(wù)隊列中取出 flushBatch 回調(diào),執(zhí)行。
  8. flushBatch 函數(shù)將 150 條日志一次性提交給 React。

于是,150 次 setState 調(diào)用,被神奇地合并成了 1 次。應(yīng)用流暢如初。

到此這篇關(guān)于React處理高頻的實時數(shù)據(jù)的解決方案的文章就介紹到這了,更多相關(guān)React處理高頻實時數(shù)據(jù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React使用useSearchParams同步URL和查詢參數(shù)的方法

    React使用useSearchParams同步URL和查詢參數(shù)的方法

    在開發(fā)React應(yīng)用時,我們經(jīng)常遇到一種場景:用戶在搜索框輸入關(guān)鍵詞,篩選出一個列表,然后希望把這個結(jié)果分享給同事,在React Router v6中,useSearchParams這個Hook就是專門用來處理這個問題的,本文將介紹如何使用它來實現(xiàn) URL 與應(yīng)用狀態(tài)的同步,需要的朋友可以參考下
    2025-12-12
  • React?高階組件與Render?Props優(yōu)缺點詳解

    React?高階組件與Render?Props優(yōu)缺點詳解

    這篇文章主要weidajai?介紹了React?高階組件與Render?Props優(yōu)缺點詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • 在React頁面重新加載時保留數(shù)據(jù)的實現(xiàn)方法總結(jié)

    在React頁面重新加載時保留數(shù)據(jù)的實現(xiàn)方法總結(jié)

    在React頁面重新加載時保留數(shù)據(jù),可以通過多種方法來實現(xiàn),常見的方法包括使用瀏覽器的本地存儲(Local Storage 或 Session Storage)、URL參數(shù)、以及服務(wù)器端存儲等,本文給大家總結(jié)了一些具體實現(xiàn)方法,需要的朋友可以參考下
    2024-06-06
  • 詳解如何優(yōu)雅地在React項目中使用Redux

    詳解如何優(yōu)雅地在React項目中使用Redux

    這篇文章主要介紹了詳解如何優(yōu)雅地在React項目中使用Redux,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • ReactJS入門實例教程詳解

    ReactJS入門實例教程詳解

    React.render?是?React?的最基本方法,用于將模板轉(zhuǎn)為?HTML?語言,并插入指定的?DOM?節(jié)點,這篇文章主要介紹了ReactJS入門實例教程,需要的朋友可以參考下
    2022-06-06
  • React組件三大核心屬性State?props?Refs介紹

    React組件三大核心屬性State?props?Refs介紹

    組件實例的三大核心屬性是:State、Props、Refs。類組件中這三大屬性都存在。函數(shù)式組件中訪問不到?this,也就不存在組件實例這種說法,但由于它的特殊性(函數(shù)可以接收參數(shù)),所以存在Props這種屬性
    2023-02-02
  • react?源碼中位運算符的使用詳解

    react?源碼中位運算符的使用詳解

    這篇文章主要為大家詳細介紹了react?位運算符,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-03-03
  • React用法之高階組件的用法詳解

    React用法之高階組件的用法詳解

    高階組件也就是我們常說的HOC,是React中用于復(fù)用組件邏輯的一種高級技巧。這篇文章主要通過一些示例帶大家學(xué)習(xí)一下高階組件的使用,希望對大家有所幫助
    2023-04-04
  • React 18版本配置rem 和 vw的詳細步驟

    React 18版本配置rem 和 vw的詳細步驟

    這篇文章主要介紹了React 18版本配置rem 和 vw的詳細步驟,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • React 全自動數(shù)據(jù)表格組件——BodeGrid的實現(xiàn)思路

    React 全自動數(shù)據(jù)表格組件——BodeGrid的實現(xiàn)思路

    表格是在后臺管理系統(tǒng)中用的最頻繁的組件之一,相關(guān)的功能有數(shù)據(jù)的新增和編輯、查詢、排序、分頁、自定義顯示以及一些操作按鈕。這篇文章主要介紹了React 全自動數(shù)據(jù)表格組件——BodeGrid ,需要的朋友可以參考下
    2019-06-06

最新評論

长垣县| 攀枝花市| 兴城市| 历史| 柘城县| 建平县| 榕江县| 新源县| 东台市| 梅州市| 泗水县| 麻阳| 连平县| 平阳县| 抚顺县| 务川| 吉隆县| 紫云| 晴隆县| 合阳县| 榕江县| 鸡东县| 融水| 永善县| 芦山县| 万载县| 新竹县| 成武县| 邳州市| 马尔康县| 台前县| 柏乡县| 镇宁| 来安县| 东方市| 唐海县| 桦甸市| 临城县| 铁岭市| 邵阳市| 聊城市|