useMemo vs useCallback:React 性能優化的兩大利器

在這里插入圖片描述

文章目錄

    • 什么是 useMemo?
      • 基本語法
      • 使用場景
      • 實際例子
    • 什么是 useCallback?
      • 基本語法
      • 使用場景
      • 實際例子
    • 核心區別對比
    • 什么時候使用它們?
      • 使用 useMemo 的時機
      • 使用 useCallback 的時機
    • 常見誤區和注意事項
      • 誤區 1:過度使用
      • 誤區 2:依賴數組不正確
      • 誤區 3:在條件語句中使用
    • 實際應用示例


在 React 開發中,性能優化始終是一個重要話題。隨著應用復雜度的增加,不必要的重新渲染和重復計算會嚴重影響用戶體驗。React 提供了兩個強大的 Hook —— useMemouseCallback 來幫助解決這些問題。雖然它們都用于性能優化,但各有不同的使用場景和目的。

什么是 useMemo?

useMemo 是一個用于緩存計算結果的 Hook。它會在依賴項發生變化時才重新計算值,否則返回上次緩存的結果。

基本語法

const memoizedValue = useMemo(() => {// 執行昂貴的計算return expensiveCalculation(a, b);
}, [a, b]); // 依賴數組

使用場景

  1. 昂貴的計算操作:避免在每次渲染時重復執行復雜計算
  2. 避免創建新的對象或數組:防止子組件不必要的重新渲染

實際例子

import React, { useMemo, useState } from 'react';function ExpensiveComponent({ items, filter }) {const [count, setCount] = useState(0);// 沒有使用 useMemo - 每次渲染都會重新計算const expensiveValue = items.filter(item => item.category === filter).reduce((sum, item) => sum + item.price, 0);// 使用 useMemo - 只有當 items 或 filter 改變時才重新計算const memoizedValue = useMemo(() => {console.log('重新計算昂貴的值');return items.filter(item => item.category === filter).reduce((sum, item) => sum + item.price, 0);}, [items, filter]);return (<div><p>計算結果: {memoizedValue}</p><p>計數: {count}</p><button onClick={() => setCount(count + 1)}>點擊計數 (不會觸發重新計算)</button></div>);
}

什么是 useCallback?

useCallback 是一個用于緩存函數的 Hook。它返回一個記憶化的回調函數,只有在依賴項發生變化時才會返回新的函數。

基本語法

const memoizedCallback = useCallback(() => {// 函數邏輯doSomething(a, b);
}, [a, b]); // 依賴數組

使用場景

  1. 傳遞給子組件的回調函數:避免子組件因為新函數引用而重新渲染
  2. 作為其他 Hook 的依賴:確保依賴數組的穩定性

實際例子

import React, { useCallback, useState, memo } from 'react';// 子組件使用 memo 包裝,只有 props 改變時才重新渲染
const ChildComponent = memo(({ onClick, name }) => {console.log(`${name} 組件渲染了`);return <button onClick={onClick}>點擊 {name}</button>;
});function ParentComponent() {const [count, setCount] = useState(0);const [name, setName] = useState('');// 沒有使用 useCallback - 每次渲染都會創建新函數const handleClick1 = () => {console.log('點擊了按鈕1');};// 使用 useCallback - 函數引用保持穩定const handleClick2 = useCallback(() => {console.log('點擊了按鈕2');}, []); // 空依賴數組,函數永遠不會改變// 依賴于 count 的回調函數const handleClick3 = useCallback(() => {console.log(`當前計數: ${count}`);}, [count]); // 只有 count 改變時才創建新函數return (<div><p>計數: {count}</p><input value={name} onChange={(e) => setName(e.target.value)}placeholder="輸入名稱"/><button onClick={() => setCount(count + 1)}>增加計數</button>{/* 每次父組件重新渲染時,子組件也會重新渲染 */}<ChildComponent onClick={handleClick1} name="按鈕1(未優化)" />{/* 只有當 handleClick2 改變時,子組件才會重新渲染 */}<ChildComponent onClick={handleClick2} name="按鈕2(已優化)" />{/* 只有當 count 改變時,子組件才會重新渲染 */}<ChildComponent onClick={handleClick3} name="按鈕3(依賴count)" /></div>);
}

核心區別對比

特性useMemouseCallback
緩存對象緩存計算結果(值)緩存函數本身
返回值返回計算后的值返回記憶化的函數
主要用途避免重復計算避免函數重新創建
典型場景復雜計算、數據處理事件處理器、回調函數
性能影響減少計算開銷減少子組件重新渲染

什么時候使用它們?

使用 useMemo 的時機

  1. 計算開銷很大:復雜的數學運算、大數據處理
  2. 創建復雜對象:避免每次渲染都創建新的對象或數組
  3. 作為其他 Hook 的依賴:確保依賴的穩定性
// ? 適合使用 useMemo
const expensiveCalculation = useMemo(() => {return heavyProcessing(largeDataSet);
}, [largeDataSet]);// ? 避免創建新對象
const userInfo = useMemo(() => ({name: user.name,email: user.email,isActive: user.status === 'active'
}), [user.name, user.email, user.status]);

使用 useCallback 的時機

  1. 傳遞給子組件的函數:特別是使用了 React.memo 的子組件
  2. 作為 useEffect 的依賴:避免 effect 不必要的重新執行
  3. 自定義 Hook 中的函數:保持 API 的穩定性
// ? 適合使用 useCallback
const handleSubmit = useCallback((formData) => {submitForm(formData);
}, []);// ? 作為 useEffect 的依賴
const fetchData = useCallback(async () => {const data = await api.getData(id);setData(data);
}, [id]);useEffect(() => {fetchData();
}, [fetchData]);

常見誤區和注意事項

誤區 1:過度使用

不是所有的計算都需要使用 useMemo,也不是所有的函數都需要使用 useCallback。這些 Hook 本身也有開銷,只有在確實需要優化時才使用。

// ? 不需要優化的簡單計算
const simpleValue = useMemo(() => a + b, [a, b]);// ? 直接計算即可
const simpleValue = a + b;

誤區 2:依賴數組不正確

確保依賴數組包含所有使用到的變量,否則可能導致 bug。

// ? 缺少依賴
const memoizedValue = useMemo(() => {return calculate(a, b, c);
}, [a, b]); // 缺少 c// ? 完整的依賴
const memoizedValue = useMemo(() => {return calculate(a, b, c);
}, [a, b, c]);

誤區 3:在條件語句中使用

Hook 必須在函數組件的頂層調用,不能在條件語句中使用。

// ? 錯誤的使用方式
if (condition) {const value = useMemo(() => calculate(), []);
}// ? 正確的使用方式
const value = useMemo(() => {if (condition) {return calculate();}return defaultValue;
}, [condition]);

實際應用示例

讓我們看一個綜合使用 useMemouseCallback 的實際例子:

import React, { useState, useMemo, useCallback, memo } from 'react';// 模擬昂貴的計算函數
const expensiveCalculation = (items) => {console.log('執行昂貴計算...');return items.reduce((sum, item) => sum + item.value * item.quantity, 0);
};// 子組件
const ProductItem = memo(({ product, onUpdate, onDelete }) => {console.log(`渲染產品: ${product.name}`);return (<div><span>{product.name}: {product.value} x {product.quantity}</span><button onClick={() => onUpdate(product.id)}>更新</button><button onClick={() => onDelete(product.id)}>刪除</button></div>);
});function ShoppingCart() {const [products, setProducts] = useState([{ id: 1, name: '商品A', value: 100, quantity: 2 },{ id: 2, name: '商品B', value: 200, quantity: 1 },{ id: 3, name: '商品C', value: 50, quantity: 3 }]);const [discount, setDiscount] = useState(0);// 使用 useMemo 緩存總價計算const totalPrice = useMemo(() => {const basePrice = expensiveCalculation(products);return basePrice * (1 - discount / 100);}, [products, discount]);// 使用 useCallback 緩存事件處理函數const handleUpdateProduct = useCallback((productId) => {setProducts(prev => prev.map(p => p.id === productId ? { ...p, quantity: p.quantity + 1 }: p));}, []);const handleDeleteProduct = useCallback((productId) => {setProducts(prev => prev.filter(p => p.id !== productId));}, []);return (<div><h2>購物車</h2>{products.map(product => (<ProductItemkey={product.id}product={product}onUpdate={handleUpdateProduct}onDelete={handleDeleteProduct}/>))}<div><label>折扣(%): <input type="number" value={discount}onChange={(e) => setDiscount(Number(e.target.value))}/></label></div><h3>總價:{totalPrice.toFixed(2)}</h3></div>);
}

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

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

相關文章

C++ 記錄

1. 字符串查找字符 bool findMap(char ch){string mapper "aeiouAEIOU";return mapper.find(ch) ! string::npos;} 2.substr函數 string substr(size_t pos 0, size_t len npos) const; 3.to_string && stoi 函數 iota 填充一個范圍&#xff0…

樸樸超市小程序 sign-v2 分析

聲明 本文章中所有內容僅供學習交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包內容、敏感網址、數據接口等均已做脫敏處理&#xff0c;嚴禁用于商業用途和非法用途&#xff0c;否則由此產生的一切后果均與作者無關&#xff01; 部分python代碼 cp execjs.compile(…

Azure 機器學習初學者指南

Azure 機器學習初學者指南 在我們的初學者指南中探索Azure機器學習&#xff0c;了解如何設置、部署模型以及在Azure生態系統中使用AutoML & ML Studio。Azure 機器學習 &#xff08;Azure ML&#xff09; 是一項全面的云服務&#xff0c;專為機器學習項目生命周期而設計&am…

HTML 從入門到起飛 · 系列合集:一站式學習不掉線

一、&#x1f4bb;計算機基礎 &#x1f31f;艾倫麥席森圖靈&#x1f31f; ?? 二戰時期&#xff0c;破譯了德軍的戰爭編碼——英格瑪。 &#x1f54a;? 讓二戰提前2年結束&#xff0c;拯救了上千萬人的生命。 &#x1f3c6; 設立圖靈獎&#xff0c;被后人譽為&#xff1a;&qu…

NodeJS的yarn和npm作用和區別,為什么建議用yarn

一、yarn和npm作用和區別 yarn異步執行安卓&#xff0c;npm同步執行安裝 yarn會復用&#xff0c;已經安裝的不會再次安裝。不過新版npm已經解決了。 Yarn安裝信息干凈一點&#xff0c;npm會羅列包信息 下面是關于 Node.js 中 npm 和 yarn 的完整對比與說明&#xff0c;幫你…

青少年編程與數學 01-011 系統軟件簡介 19 SSMS 數據庫管理工具

青少年編程與數學 01-011 系統軟件簡介 19 SSMS 數據庫管理工具 一、歷史發展二、技術特色三、主要功能四、應用場景五、產品版本六、使用方法七、市場地位八、發展前景全文總結 **摘要&#xff1a;**SSMS 是微軟開發的數據庫管理工具&#xff0c;首次引入于 SQL Server 2005&a…

React配置別名路徑完整指南

文章目錄 什么是別名路徑配置方法1. Create React App 項目方法一&#xff1a;使用jsconfig.json&#xff08;推薦&#xff09;方法二&#xff1a;使用CRACO&#xff08;React App Rewired的替代方案&#xff09; 2. Vite 項目修改vite.config.js配置TypeScript支持 3. Next.js…

本周大模型新動向:自主機器學習、狀態機推理、上下文壓縮

點擊藍字 關注我們 AI TIME歡迎每一位AI愛好者的加入&#xff01; 01 Sentinel: Attention Probing of Proxy Models for LLM Context Compression with an Understanding Perspective 檢索增強型生成&#xff08;RAG&#xff09;通過外部上下文增強了大型語言模型&#xff08;…

從零開始掌握 Docker:核心命令與實踐指南

從零開始掌握 Docker&#xff1a;核心命令與實踐指南 摘要&#xff1a; 本文系統整理了 Docker 的核心概念與常用命令&#xff0c;涵蓋鏡像管理、容器操作、數據存儲、網絡配置、Compose 編排以及 Dockerfile 構建等內容。通過清晰的命令示例和實用說明&#xff0c;幫助開發者…

RDMA簡介7之RoCE v2可靠傳輸

可靠傳輸技術旨在通過多種方法確保數據包在傳輸過程中不會丟失或損壞&#xff0c;同時保證數據包按發送順序到達接收端&#xff0c;其要求在鏈路發生丟包或網絡發生擁塞等情況下能夠完全保證數據包的正確性同時盡可能地提高傳輸速率。RoCE v2協議實現可靠傳輸的技術手段共有三種…

java33

1.特殊文件 屬性文件properties 配置文件xml 注意&#xff1a;XML文件可以直接在瀏覽器里面打開&#xff1a; 了解知識&#xff1a; 2.日志技術 3.單元測試 注意&#xff1a;點擊方法內部再右鍵運行是測試改方法的&#xff0c;點擊類名再右鍵運行才是測試整個測試類的&#xff…

OSI 七層網絡模型

目錄 OSI 七層網絡模型七層模型1. 物理層&#xff08;Physical Layer&#xff09;2. 數據鏈路層&#xff08;Data Link Layer&#xff09;3. 網絡層&#xff08;Network Layer&#xff09;4. 傳輸層&#xff08;Transport Layer&#xff09;5. 會話層&#xff08;Session Layer…

Spring Boot 4.0.0 新特性詳解:深入解讀 Spring Framework 7.0.0

你是否注意到創建新 Spring Boot 項目時出現的最新選項?Spring Boot 4.0.0 預覽版現已發布,基于最新的 Spring Framework 7.0.0 ??。這個版本引入了眾多激動人心的新特性,不僅提升了開發效率,改善了空值安全性,還簡化了 Web 應用程序的開發流程。本文將深入探討這些重要…

從0到1構建高并發秒殺系統:實戰 RocketMQ 異步削峰與Redis預減庫存

&#x1f680;從0到1構建高并發秒殺系統&#xff1a;實戰 RocketMQ 異步削峰與Redis預減庫存 &#x1f4d6;一、 簡介 在電商、搶票等高并發場景中&#xff0c;秒殺系統面臨著“高并發、庫存稀缺、易超賣、系統易崩”的嚴峻挑戰。傳統的同步處理架構難以支撐海量請求并發下的性…

OpenCV CUDA模塊圖像變形------對圖像進行任意形式的重映射(Remapping)操作函數remap()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 該函數根據給定的映射表 xmap 和 ymap 對圖像進行 任意形式的重映射&#xff08;Remapping&#xff09;操作&#xff0c;是 GPU 加速版本的圖像幾…

PC 端常用 UI 組件庫

一、前言 隨著企業級應用、后臺管理系統、數據平臺等項目的不斷發展&#xff0c;前端開發已經不再局限于移動端和響應式布局&#xff0c;而是越來越多地聚焦于 PC 端系統的構建。為了提升開發效率、統一設計風格并保障用戶體驗&#xff0c;使用成熟的 UI 組件庫 成為了現代前端…

pikachu靶場通關筆記31 文件包含02之遠程文件包含

目錄 一、文件包含功能 二、文件包含Vulnerability 二、遠程文件包含 三、環境配置 1、進入靶場 2、搭建環境 &#xff08;1&#xff09;定位php.ini文件 &#xff08;2&#xff09;修改php.ini文件 四、源碼分析 五、滲透實戰 1、選擇科比 2、執行phpinfo &…

QT集成Boost庫

在Windows平臺上&#xff0c;使用Qt集成Boost庫&#xff0c;并基于MSVC編譯器在CMake文件中加載&#xff0c;可以按照以下步驟進行配置。 Boost庫的編譯 如果Boost庫未預編譯&#xff0c;需要手動編譯&#xff0c;解壓zip到D:\Library\boost_1_87_0&#xff0c;打開cmd命令行…

MySQL從庫復制延遲的監測

目錄 ?? 一、原生內置方法?? 二、心跳表工具&#xff08;如pt-heartbeat&#xff09;?? 三、MySQL 8.0 增強方案&#x1f4ca; 四、各方案對比總結&#x1f48e; 五、選擇建議 MySQL從庫復制延遲的監測是保障數據一致性和讀寫分離可靠性的關鍵環節&#xff0c;以下是主流…

slam--最小二乘問題--凹凸函數

最小二乘問題 最小二乘問題標準公式 殘差函數&#xff0c;線性和非線性最小二乘 最小二乘問題的兩種寫法&#xff1a; 目標 找到 x使得預測值 Ax與觀測值 b 的殘差平方和最小。 范數和范數平方 線性最小二乘 一般形式&#xff1a; 殘差 rAx?b是x 的線性函數。 目標函數是…