React 閉包陷阱及解決方案與 React 16/17/18 版本區別

一、React 閉包陷阱詳解

1. 什么是閉包陷阱

React 閉包陷阱是指在函數組件中使用 Hook(特別是?useEffect?和?useCallback)時,由于閉包特性導致訪問到舊的 state 或 props 值,而非最新值的現象。

2. 典型場景示例

function Counter() {const [count, setCount] = useState(0);useEffect(() => {const timer = setInterval(() => {console.log(count); // 這里總是打印初始值0setCount(count + 1);}, 1000);return () => clearInterval(timer);}, []); // 空依賴數組return <div>{count}</div>;
}

3. 產生原因分析

  • 閉包特性:Effect 回調函數捕獲了初始渲染時的?count?值

  • 依賴數組:空數組意味著 Effect 只在掛載時運行一次

  • 每次渲染獨立:每次渲染都有獨立的 props/state 和 Effect

二、解決閉包陷阱的 5 種方法

1. 正確聲明依賴項

useEffect(() => {const timer = setInterval(() => {setCount(count + 1);}, 1000);return () => clearInterval(timer);
}, [count]); // 添加count依賴

2. 使用函數式更新

useEffect(() => {const timer = setInterval(() => {setCount(prev => prev + 1); // 使用前一個值}, 1000);return () => clearInterval(timer);
}, []); // 不需要添加count依賴

3. 使用 useRef 保存可變值

function Counter() {const [count, setCount] = useState(0);const countRef = useRef(count);useEffect(() => {countRef.current = count; // 每次渲染更新ref});useEffect(() => {const timer = setInterval(() => {console.log(countRef.current); // 訪問最新值setCount(countRef.current + 1);}, 1000);return () => clearInterval(timer);}, []);return <div>{count}</div>;
}

4. 使用 useReducer

function reducer(state, action) {if (action.type === 'tick') {return state + 1;}throw new Error();
}function Counter() {const [state, dispatch] = useReducer(reducer, 0);useEffect(() => {const timer = setInterval(() => {dispatch({ type: 'tick' });}, 1000);return () => clearInterval(timer);}, []); // 不需要依賴return <div>{state}</div>;
}

5. 自定義 Hook 封裝

function useInterval(callback, delay) {const savedCallback = useRef();useEffect(() => {savedCallback.current = callback;});useEffect(() => {function tick() {savedCallback.current();}if (delay !== null) {const id = setInterval(tick, delay);return () => clearInterval(id);}}, [delay]);
}// 使用
function Counter() {const [count, setCount] = useState(0);useInterval(() => {setCount(count + 1);}, 1000);return <div>{count}</div>;
}

三、React 16/17/18 主要區別

React 16.x 主要特性

  1. Fiber 架構(16.0)

    • 新的協調算法

    • 支持異步渲染(但默認未啟用)

  2. Hooks(16.8)

    • useState, useEffect 等 Hook API

    • 函數組件能力大幅增強

  3. Error Boundaries(16.0)

    • componentDidCatch 生命周期

    • 更好的錯誤處理機制

  4. Context API(16.3)

    • 新的 Context API 設計

    • 替代舊版 context

React 17.x 主要特性

  1. 事件委托變更(17.0)

    • 事件不再附加到 document,而是附加到 root DOM

    • 解決多版本 React 共存問題

  2. 漸進式升級(17.0)

    • 更容易逐步升級 React 版本

    • 為 React 18 做準備

  3. 新的 JSX 轉換(17.0)

    • 無需引入 React 即可使用 JSX

    • 自動從 react/jsx-runtime 導入

  4. Effect 清理時機(17.0)

    • useEffect 清理函數改為異步執行

    • 與 componentWillUnmount 行為一致

React 18.x 主要特性

  1. 并發渲染(18.0)

    • 新的 createRoot API

    • startTransition, useTransition

    • useDeferredValue

  2. 自動批處理(18.0)

    • 自動合并多個狀態更新

    • 包括 Promise、setTimeout 等

  3. 新的 Hook(18.0)

    • useId:生成唯一 ID

    • useSyncExternalStore:外部存儲集成

    • useInsertionEffect:CSS-in-JS 庫使用

  4. 流式 SSR(18.0)

    • Suspense 支持服務端渲染

    • 選擇性注水(Selective Hydration)

四、版本升級對比表

特性React 16React 17React 18
架構FiberFiber并發Fiber
Hook支持16.8+
事件系統documentrootroot
批處理僅React事件僅React事件全自動
SSR傳統傳統流式+選擇性注水
新APIContext, Error BoundariesTransition, Suspense等
JSX轉換經典新/經典
默認渲染模式同步同步并發可選

五、實際開發建議

  1. 閉包陷阱防范

    • 始終檢查 Hook 依賴數組

    • 優先使用函數式更新

    • 復雜場景使用 useReducer

  2. 版本選擇建議

    • 新項目直接使用 React 18

    • 現有項目逐步升級到 18(通過 17 過渡)

    • 需要 IE11 支持的項目可停留在 17

  3. 升級注意事項

    # 從16/17升級到18的步驟
    npm install react@18 react-dom@18
    # 修改入口文件
    import { createRoot } from 'react-dom/client';
    const root = createRoot(document.getElementById('root'));
    root.render(<App />);
  4. 并發特性采用策略

    // 逐步采用并發特性
    function App() {const [isPending, startTransition] = useTransition();const handleClick = () => {startTransition(() => {// 非緊急狀態更新setResource(fetchData());});};return (<Suspense fallback={<Spinner />}><Component /></Suspense>);
    }

理解 React 閉包陷阱和版本差異有助于編寫更健壯的 React 應用,并合理規劃項目升級路線。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917338.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917338.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917338.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[BJDCTF2020]EasySearch

首先嘗試了一下sql注入&#xff0c;但是沒有找到不同回顯。直接用sqlmap掃描一下&#xff0c;因為這邊用的是POST請求&#xff0c;所以需要抓包將請求復制到txt文件中然后使用命令sqlmap -p bp.txt。也沒有發現注入漏洞。 再進行目錄掃描試試&#xff1a; [02:33:43] 403 - …

【Linux】基本指令的使用 and 面試常問

1、man 指令使用方法&#xff1a;man Linux指令。功能&#xff1a;相當于字典&#xff0c;查找指令的用法。常用選項&#xff1a;-k&#xff1a;根據關鍵字搜索聯機幫助。num&#xff1a;只在第num章節查找。-a&#xff1a;將所有章節的都顯示出來&#xff0c;比如man printf它…

零基礎 “入坑” Java--- 十六、字符串String 異常

文章目錄一、String1.字符串的不可變性2.字符串的修改3.StringBuilder和StringBuffer4.【字符串練習】4.1 字符串中的第一個唯一字符4.2 字符串最后一個單詞的長度4.3 驗證回文串二、異常1.初識異常2.異常的分類3.異常的處理4.異常處理流程總結5.自定義異常在上一章節中&#x…

梯度下降在大模型訓練中的作用與實現

梯度下降&#xff08;Gradient Descent&#xff09;是深度學習中最核心的優化算法之一。大模型&#xff08;如GPT、BERT&#xff09;在訓練時需要優化數十億甚至上千億的參數&#xff0c;而梯度下降及其變體&#xff08;如SGD、Adam&#xff09;正是實現這一優化的關鍵工具。它…

【JVS更新日志】開源框架、APS排產、企業計劃、物聯網、邏輯引擎7.30更新說明!

項目介紹 JVS是企業級數字化服務構建的基礎腳手架&#xff0c;主要解決企業信息化項目交付難、實施效率低、開發成本高的問題&#xff0c;采用微服務配置化的方式&#xff0c;提供了低代碼數據分析物聯網的核心能力產品&#xff0c;并構建了協同辦公、企業常用的管理工具等&…

Eclipse中導入新項目,右鍵項目沒有Run on Server,Tomcat的add and remove找不到項目

原因分析沒有勾選Dynamic Web Module、Java、JavaScriptDynamic Web Module版本問題解決方法Eclipse中右鍵項目選擇Properties左側點擊project facets勾選Dynamic Web Module、Java、JavaScript&#xff0c;注意Dynamic Web Module版本問題,要和tomcat版本對應。- Dynamic Web …

IntelliJ IDEA 2025系列通用軟件安裝教程(Windows版)

前言 JetBrains系列開發工具&#xff08;如IntelliJ IDEA、PyCharm、WebStorm等&#xff09;是程序員們非常喜愛的集成開發環境。2025年最新版本帶來了更多強大的功能和改進。本教程將詳細介紹如何在Windows系統上安裝JetBrains 2025系列軟件。 最近挖到一個寶藏級人工智能學習…

烏鶇科技前端二面

1. 你能給我介紹一下你參與的重要項目&#xff0c;并重點介紹一下做的內容?通俗解釋&#xff1a; 挑一個你覺得最拿得出手、技術含量最高的項目&#xff0c;說說這個項目是干什么的&#xff08;比如一個電商網站、一個后臺管理系統&#xff09;&#xff0c;你在里面具體負責了…

《c++面向對象入門與實戰》筆記

前年的書&#xff0c;翻出來整理一下7章.指針指針 sizeof為4*指針 sizeof為 所指類型的sizeof注意free后置空&#xff0c;避免野指針11章.類

easyExcel生成多個sheet的動態表頭的實現

在使用 EasyExcel 實現“多個 Sheet 且每個 Sheet 表頭是動態的”需求時&#xff0c;思路如下&#xff1a;? 實現思路概述 EasyExcel 的 ExcelWriter 支持多個 Sheet 寫入。每個 Sheet&#xff1a; 使用 WriteSheet 創建&#xff1b;可以綁定一個動態生成的表頭 List<List&…

SQL 連接類型示例:內連接與外連接

SQL 連接類型示例&#xff1a;內連接與外連接 示例數據表 假設我們有兩個表&#xff1a; employees 表:emp_idemp_namedept_id1張三1012李四1023王五1034趙六NULLdepartments 表:dept_iddept_name101銷售部102技術部104財務部1. 內連接 (INNER JOIN) 內連接只返回兩個表中匹配的…

Ubuntu安裝gpu驅動,cuda

系統初始化 1、安裝基礎軟件 apt-get update apt-get -y install openssh-server openssh-client apt-utils freeipmi ipmitool sshpass ethtool zip unzip nano less git netplan.io iputils-ping mtr ipvsadm smartmontools python3-pip socat conntrack libvirt-clients li…

ctfshow_源碼壓縮包泄露

根據題目信息直接dirsearch解壓下來一個.txt文件&#xff0c;一個index.phpflag{flag_here}不對那么就去看index.php也沒有東西&#xff0c;于是查看wp發現是訪問/fl000g.txt這才是對的還有很多源碼泄露需要去了解? git源碼泄露? svn源碼泄露? DS_Store 文件泄露? 網站備份…

Python 程序設計講義(54):Python 的函數——函數概述

Python 程序設計講義&#xff08;54&#xff09;&#xff1a;Python 的函數——函數概述 目錄Python 程序設計講義&#xff08;54&#xff09;&#xff1a;Python 的函數——函數概述一、函數的類型1、內置函數2、自定義函數二、調用函數Python 提供了函數機制&#xff0c;把實…

學習Python中Selenium模塊的基本用法(3:下載瀏覽器驅動續)

前一篇文章主要介紹下載針對火狐瀏覽器的WebDriver&#xff0c;寫那篇文章時才找到能夠下最新版本Chrome的WebDriver地址&#xff08;參考文獻6&#xff09;&#xff0c;本文繼續學習并驗證針對Chrome瀏覽器的WebDriver下載和使用方法。Chrome的WebDriver版本與操作系統相關&am…

AIDL當Parcelable序列化的數據類通信時報“Class not found when unmarshalling“找不到該類時的解決方案

1. 報錯棧 &#xff1a;cusText這個類找不到 2 16:01:29.796 1044 5718 E Parcel : Class not found when unmarshalling: com.cus.sdk.cusText 08-02 16:01:29.796 1044 5718 E Parcel : java.lang.ClassNotFoundException: com.cus.sdk.cusText 08-02 16:01:29.796 1…

Django模型查詢與性能調優:告別N+1問題

文章目錄一、查詢基礎QuerySet 詳解一對多關聯查詢多對多關聯查詢二、N1查詢問題問題分析檢測方法解決方案三、高級查詢優化values()values_list()values()和values_list()對比Q() 對象復雜查詢查看生成的 SQL四、項目實戰場景實戰一、查詢基礎 QuerySet 詳解 Django 中通過模…

PyTorch 中 Tensor 統計學函數及相關概念

文章目錄PyTorch 中 Tensor 統計學函數及相關概念一、引言二、基礎統計學函數&#xff08;一&#xff09;torch.mean()——均值計算&#xff08;二&#xff09;torch.sum()——總和計算&#xff08;三&#xff09;torch.prod()——元素積計算&#xff08;四&#xff09;torch.m…

淺拷貝與深拷貝的區別

淺拷貝和深拷貝是兩種不同的對象復制方式&#xff0c;主要區別在于它們如何處理對象內部的引用類型字段。淺拷貝 (Shallow Copy)特點&#xff1a;只復制對象本身&#xff08;基本類型字段&#xff09;和對象中的引用&#xff08;地址&#xff09;不復制引用指向的實際對象原始對…

腳本統計MongoDB集合表數據量

腳本&#xff1a; #!/bin/bashipxxx.xx.xx.xx portxxxx dbxxxdb #user #passwmongo -host ${ip}:${port} <<EOF 2>/dev/null|grep -vE version|not match|session|compressors||Warning|delivers|upcoming|installation|https|switched|bye >collec use ${db}; sho…