React(四)memo、useCallback、useMemo Hook

目錄

(一)memo API

1.先想一個情景

2.用法

(1)props傳入普通數據類型的情況?

(2)props傳入對象的情況

(3)props傳入函數的情況

(4)使用自定義比較函數

3.什么時候使用memo?

(二)useMemo Hook

1.用法

2.useMemo實現組件記憶化?

3.useMemo實現函數記憶化

(三)useCallback Hook

1.用法

(四)總結


(一)memo API

memo – React 中文文檔

1.先想一個情景

function App() {const [count, setCount] = useState(0)const [name, setName] = useState('csq')return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name name={name}></Name></>)
}function Name(props){console.log('Name組件重新渲染了');return (<div>{props.name}</div>)
}

點擊按鈕count改變后,整個App組件會重新渲染,即使Name組件的props沒有改變,Name組件也被重新渲染了

如果一個過于復雜的組件的props沒有改變,那么重新渲染它會增加渲染負擔;

通常情況下,只要該組件的 props 沒有改變,這個記憶化版本就不會在其父組件重新渲染時重新渲染。這就稱之為記憶化,是一種性能優化的辦法

因此,memo api的作用就體現出來了:

memo 允許你的組件在 props 沒有改變的情況下跳過重新渲染。?

2.用法

const xxx = memo(function xxx(props){...}, arePropsEqual?)?

將需要進行記憶化的組件用memo包裹起來,通過arePropsEqual函數判斷props是否變化(可自定義,不寫就默認使用Object.is()判斷),然后返回新的react組件

(1)props傳入普通數據類型的情況?

import { memo } from 'react'
// 使用memo
function App() {const [count, setCount] = useState(0)const [name, setName] = useState('csq')return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name name={name}></Name></>)
}// 不傳入arePropsEqual函數就默認用Object.is判斷
const Name = memo(function Name(props){console.log('Name組件重新渲染了');return (<div>{props.name}</div>)
})

這樣一來,Name組件在父組件重新渲染的時候就不會跟著渲染啦

(2)props傳入對象的情況

上面提到了,不自定義比較函數的話,memo采用Object.is()方法來比較props是否變化

但是!Object.is()只能比較淺層數據是否變化,如果對復雜數據類型進行比較會發現:

結果為false!這表明了傳入復雜數據類型的prop仍會導致memo組件重新渲染?

import { memo } from 'react'
// 使用memo
function App() {const [count, setCount] = useState(0)return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name data={{name:'csq'}}></Name></>)
}
// 不傳入arePropsEqual函數就默認用Object.is判斷
const Name = memo(function Name(props){console.log('Name組件重新渲染了',props);return (<div>{props.data.name}</div>)
})

重新渲染了又?

如何避免??

為了最大化使用 memo 的效果,應該盡量減少 props 的變化次數?

  1. 最小化props:將對象拆分開再傳
  2. 使用useMemo Hook(后面會講到)
  3. 指定自定義比較函數(后面會講到)

(3)props傳入函數的情況

函數也是復雜數據類型中的一種,所以傳入的就算是同一個函數,父組件的重新渲染也會引起記憶化的組件重新渲染,這樣memo就失效了!

針對props傳入的函數,就要采用另一個Hook:useCallback?來實現組件的記憶化,后面第三節就會講到了

(4)使用自定義比較函數

一定要確保使用比較函數的時間比重新渲染要快,不然寫個比較函數還浪費那么久時間簡直白白干了

const Chart = memo(function Chart({ dataPoints }) {// ...
}, arePropsEqual);function arePropsEqual(oldProps, newProps) {return (oldProps.dataPoints.length === newProps.dataPoints.length &&oldProps.dataPoints.every((oldPoint, index) => {const newPoint = newProps.dataPoints[index];return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;}));
}

注意:比較函數內一定要將props的所有prop都比較到,包括函數。

避免在 arePropsEqual 中進行深比較,除非你 100% 確定你正在處理的數據結構具有已知有限的深度。深比較可能會變得非常緩慢,并且如果有人稍后更改數據結構,這可能會卡住你的應用數秒鐘。

簡而言之就是少用,有那個功夫用用hook肯定更快啦?

3.什么時候使用memo?

memo api并不是是個組件就給安上的,首先要考慮到組件的重新渲染對頁面會不會造成影響,比如有動畫的組件:重新渲染會導致動畫重新播放,如果不想變化的話就加個memo

如果組件的重新渲染沒有什么延遲,當然也不需要用memo?


(二)useMemo Hook

useMemo – React 中文文檔

useMemo 能在每次重新渲染的時候能夠緩存計算的結果。

1.用法

useMemo(calculateValue, dependencies)?

  • calculateValue:要緩存計算值的函數。React 將會在首次渲染時調用該函數;在之后的渲染中,如果 dependencies 沒有發生變化,React 將直接返回相同值。否則,將會再次調用 calculateValue 并返回最新結果,然后緩存該結果以便下次重復使用。

  • dependencies:所有在 calculateValue 函數中使用的響應式變量組成的數組。React 使用 Object.is 將每個依賴項與其之前的值進行比較。?

舉例:用counterFilter模擬運行時間長的函數,每次點擊todo長度加1按鈕都會等待0.5s

不使用useMemo的話,即使len不發生改變,count發生改變時,也會重新渲染運行counterFilter

使用useMemo()后,當len發生改變時,才會重新計算todoSlice的值,而點擊count+1按鈕不會造成卡頓

import { useState, useMemo } from 'react'
function App() {const [count,setCount] = useState(1)const [len, setLen] = useState(2)// 這里todos是個數組,也過不了Object.is(),所以套一層useMemoconst todos = useMemo(()=>([1, 2, 3, 4, 5, 6, 7, 8, 9]),[])const todoSlice = useMemo(() => counterFilter(todos, len),[todos, len])return (<><button onClick={() => { setLen(len + 1) }}>todo長度加1</button><button onClick={() => { setCount(count + 1) }}>count加1</button>{todoSlice.map(value => (<p key={value}>todo: {value}</p>))}<Counter count={count}></Counter></>)
}const Counter = function Name(props) {return (<div>{props.count}</div>)
}function counterFilter(todos, length) {let startTime = performance.now();while (performance.now() - startTime < 500) {// 在 500 毫秒內不執行任何操作以模擬極慢的代碼}return todos.filter((value, index) => index < length)
}

這樣一用,哎喲這不是vue的計算屬性嘛,蠻有意思蠻有意思

不過useMemo Hook設計來是用于存儲運算時間長的計算結果以避免重復渲染的一種優化手段(我自認為是這樣

2.useMemo實現組件記憶化?

提到避免重復渲染,useMemo可以結合上文的memo api實現記憶化,解決props傳入對象破環記憶化的情況

function Page() {const [name, setName] = useState('Taylor');const [age, setAge] = useState(42);const person = useMemo(() => ({ name, age }),[name, age]);return <Profile person={person} />;
}const Profile = memo(function Profile({ person }) {// ...
});

3.useMemo實現函數記憶化

函數也是一種對象,所以當然能用useMemo實現記憶化?

export default function Page({ productId, referrer }) {const handleSubmit = useMemo(() => {return (orderDetails) => {post('/product/' + productId + '/buy', {referrer,orderDetails});};}, [productId, referrer]);return <Form onSubmit={handleSubmit} />;
}

這樣看起來蠢蠢的,函數又套函數!

術業有專攻,記憶函數要使用react專門提供的一個hook,也就是下面要講到的


(三)useCallback Hook

useCallback – React 中文文檔

useCallback 是一個允許你在多次渲染中緩存函數的 React Hook。?

1.用法

const cachedFn = useCallback(fn, dependencies)

用法和useMemo一樣,傳入兩個參數

  • fn:想要緩存的函數。React 將會在初次渲染而非調用時返回該函數。當進行下一次渲染時,如果 dependencies 相比于上一次渲染時沒有改變,那么 React 將會返回相同的函數。否則,React 將返回在最新一次渲染中傳入的函數,并且將其緩存以便之后使用。React 不會調用此函數,而是返回此函數。你可以自己決定何時調用以及是否調用。

  • dependencies:有關是否更新 fn 的所有響應式值的一個列表。響應式值包括 props、state,和所有在你組件內部直接聲明的變量和函數。

  • 返回值:返回想要緩存的函數。

還是用memo那兒的例子:

函數setName本質是對象,即使傳入的函數沒有改變,Object.is()判斷也是false,這樣就導致memo失去了記憶化

function App() {const [count, setCount] = useState(0)const [name, setName] = useState('csq')return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name name={name} setName={(name)=>{setName(name)}}></Name></>)
}const Name = memo(function Name(props){console.log('Name組件重新渲染了');return (<><div>{props.name}</div><button onClick={() => { props.setName(props.name+'!') }}>修改名字</button></>)
})

直接上useCallback!

這個useCallback里的依賴數組為空,是因為setName這個函數本身就是不變的,不受變量影響

import { useState, memo, useCallback } from 'react'
function App() {const [count, setCount] = useState(0)const [name, setName] = useState('csq')const setNameCallback = useCallback((name) => { setName(name) },[])return (<><div>{count}</div><button onClick={()=>{setCount(count+1)}}>加1</button><Name name={name} setName={setNameCallback}></Name></>)
}const Name = memo(function Name(props){console.log('Name組件重新渲染了');return (<><div>{props.name}</div><button onClick={() => { props.setName(props.name+'!') }}>修改名字</button></>)
})

(四)總結

今天學了memo、useMemo、useCallback,感覺花太多時間在memo上了,后面兩個hook就寫的稍微有點水,有點漏洞

以后應該多花點時間在敲代碼上,感覺寫博客寫太久了

memo:用于實現組件記憶化,如果props傳遞的是對象,那么memo就沒意義了

useMemo:立即執行函數返回結果并保存。可以和memo結合使用,傳遞props不變則不重新渲染;也可以用于存儲較花費事件的數據;

useCallback:和memo結合使用,是保存函數但不會執行。

總結來說都是用于性能優化,?不能依靠這些來避免本身代碼的問題

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

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

相關文章

如何停止 iPad 和 iPhone 之間共享短信,獨立接收和發送消息

概括 在當今高度互聯的數字世界中&#xff0c;Apple 設備之間的無縫連接性提供了極大的便利&#xff0c;尤其是在消息同步方面。iPhone 和 iPad 用戶通常可以享受到設備間短信的自動同步功能&#xff0c;這意味著無論是在哪個設備上&#xff0c;用戶都可以接收和回復消息。然而…

2024.5.26.python.exercise

# # 導入包 # from pyecharts.charts import Bar, Timeline # from pyecharts.options import LabelOpts, TitleOpts # from pyecharts.globals import ThemeType # # # 從文件中讀取信息 # GDP_file open("1960-2019全球GDP數據.csv", "r", encoding&quo…

A. Maximize?

time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output You are given an integer x&#x1d465;. Your task is to find any integer y&#x1d466; (1≤y<x)(1≤&#x1d466;<&#x1d465;) su…

深入理解python列表與字典:數據結構的選擇與性能差異

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、列表與字典&#xff1a;基礎數據結構的對比 二、列表&#xff1a;逐個遍歷的查找方式 …

Ceres求解優化問題

1. 簡介 Ceres Solver是專門用于求解非線性最小二乘問題的C開源庫,研究SLAM方向不過濾波和優化兩個技術路線,因此常用Ceres庫解決實際項目中的優化問題,當然還有g2o同樣可用,但就說明文檔而言,Ceres對新用戶更友好,g2o提供不多的文檔,更多是需要參考其它開源項目使用,所以筆者…

【JAVA】接口

前面我們說了說抽象類相關內容&#xff0c;這篇我們主要聊聊接口相關內容&#xff0c;這部分很重要&#xff0c;大家引起關注。 1. 接口 1.1 接口的概念 接口就是公共的行為規范標準&#xff0c;大家在實現時&#xff0c;只要符合規范標準&#xff0c;就可以通用。在Java中&am…

力扣 739. 每日溫度 python AC

單調棧 class Solution:def dailyTemperatures(self, temperatures):size len(temperatures)ll []ans [0] * sizefor i in range(size - 1, -1, -1):while ll and temperatures[i] > temperatures[ll[-1]]:ll.pop()if ll:ans[i] ll[-1] - ill.append(i)return ans

C語言 數組——向函數傳遞數組

目錄 把數組傳給函數&#xff08;Passing Arrays to Functions&#xff09; 向函數傳遞一維數組 向函數傳遞二維數組 數組在學生成績管理中的應用 例&#xff1a;計算每個學生的平均分 把數組傳給函數&#xff08;Passing Arrays to Functions&#xff09; 向函數傳遞一維…

gnocchi學習小結

背景 總結gnocchi 4.4版本gnocchi-metricd工作流程 入口 gnocchi.cli.metricd metricd stop after processing metric默認為0&#xff0c;調servicemanager run MetricdServiceManager __init__ 服務邏輯封裝到MetricdServiceManager初始化中 主要由MetricProcessor, Met…

基于Vue的前端自定義詢問彈框與輸入彈框組件的設計與實踐

基于Vue的前端自定義詢問彈框與輸入彈框組件的設計與實踐 摘要 隨著技術的不斷進步&#xff0c;前端開發面臨越來越多的挑戰&#xff0c;其中之一就是如何有效管理復雜的業務邏輯和用戶體驗。傳統的整塊應用開發方式在面對頻繁的功能變更和用戶體驗優化時&#xff0c;往往顯得…

python數據分析-CO2排放分析

導入所需要的package import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import datetime %matplotlib inline plt.rcParams[font.sans-serif] [KaiTi] #中文 plt.rcParams[axes.unicode_minus] False #負號 數據清洗…

MySQL數據表索引命名規范

在數據庫設計和開發過程中&#xff0c;索引是提高查詢性能的重要工具。合理的索引命名規范不僅能提高代碼的可讀性&#xff0c;還能便于維護和管理。本文將詳細介紹MySQL數據表索引的命名規范&#xff0c;包括不同類型索引的命名方法&#xff0c;并提供多個代碼示例以說明如何命…

SSH 遠程登錄系統和遠程拷貝

文章目錄 目錄 文章目錄 前言 一.SSH的基本用法 SSH基本用法&#xff1a; SSH無密碼登錄 二.SSH安全設置 三.SSH限制用戶 前言 很多時候服務器并沒有服務器&#xff0c;我們也不能每次都通過控制臺去管理服務器&#xff0c;這時候就需要遠程登錄&#xff0c;相比于Telnet&a…

京東應屆生公司內網說了一句‘什么時候被pdd收購‘,結果慘遭辭退

京東應屆生公司內網說了一句’什么時候被pdd收購’&#xff0c;結果慘遭公司開除 這個事最近在圈子討論比較多 前二天&#xff0c;有一個上海交大畢業的應屆生&#xff0c;在京東實習了9個月&#xff0c;好不容易轉正12天后&#xff0c;只因在內網說了一句話&#xff0c;就被…

upload-labs 21關解析

目錄 一、代碼審計 二、實踐 三、總結 一、代碼審計 $is_upload false; $msg null; if(!empty($_FILES[upload_file])){//檢查MIME$allow_type array(image/jpeg,image/png,image/gif);if(!in_array($_FILES[upload_file][type],$allow_type)){$msg "禁止上傳該類型…

一個程序員的牢獄生涯(38)答案

星期一 答 案 我被這個不知道什么時候無聲無息的出現在身后的人嚇出了一身的冷汗。 看到我發現了他,這個人慢慢地抬起了頭……“他X的,是小X州!” 此時的小X州臉上并沒有著急等待上廁所的表情,反而是用一種狡黠的眼神看著我。一直充滿的敵意,現在又多了一絲威脅的神情,讓…

Quartus Cyclone I II III IVE 器件型號

玩耍了一個 EP2 型號的開發板&#xff0c;發現 安裝的quartus13 沒有Cyclone II 型號&#xff0c;經過探索發現了是版本不對。 https://www.intel.com/content/www/us/en/software-kit/711920/intel-quartus-ii-subscription-edition-design-software-version-13-0sp1-for-win…

行業分析---造車新勢力之蔚來汽車

1 前言 在之前的博客中&#xff0c;筆者分析了蘋果《行業分析---我眼中的Apple Inc.》&#xff0c;蘋果已經成為世界級的公司。隨后也分析了電動汽車公司特斯拉《行業分析---馬斯克的Tesla》&#xff0c;特斯拉也在不斷成長。目前能分析的新能源汽車公司不多&#xff0c;小米汽…

Minecraft服務器如何搭建

Minecraft這是原版英文名稱&#xff0c;在中國大陸被譯為《我的世界》&#xff0c;這款游戲很火爆。臺灣的很多小伙伴也在玩&#xff0c;其譯名為《我的創世神》。現在這款游戲在國內已經被網易代理了。因為這款游戲開源&#xff0c;所以任何人都可以搭建服務器端&#xff0c;如…

機器人支持回調接口配置(詳細教程)

大家伙&#xff0c;我是雄雄&#xff0c;歡迎關注微信公眾號&#xff1a;雄雄的小課堂。 一、前言 今天&#xff0c;給大家介紹一下&#xff0c;如何在機器人中配置回調地址和接口編寫。很多時候我們可能有這樣的場景&#xff0c;收到消息后&#xff0c;想自己處理一下消息的內…