useMemo和useEffect有什么區(qū)別栈妆?怎么使用useMemo

本文可能會讓你感覺到很啰嗦涕蜂,我想盡量線索清晰析校,但是好像有點失敗...

1. 背景

先看下面這段代碼

import React, {Fragment} from 'react'
import { useState, useMemo } from 'react'

// 產(chǎn)品名稱列表
const nameList = ['apple', 'peer', 'banana', 'lemon']

const example = (props) => {
    // 產(chǎn)品名稱、價格
    const [price, setPrice] = useState(0)
    const [name, setName] = useState('apple')
  
    // 假設(shè)有一個業(yè)務(wù)函數(shù)  獲取產(chǎn)品的名字
    function getProductName() {
        console.log('getProductName觸發(fā)')
        return name
    }

    return (
        <Fragment>
            <p>{name}</p>
            <p>{price}</p>
            <p>{getProductName()}</p>
            <button onClick={() => setPrice(price+1)}>價錢+1</button>
            <button onClick={() => setName(nameList[Math.random() * nameList.length << 0])}>修改名字</button>
        </Fragment>
    )
}
export default example


現(xiàn)在問幾個問題:
發(fā)生下面幾種情況會重新渲染界面嗎(也就是getProductName函數(shù)會被觸發(fā))长酗?

  1. 點擊價錢+1按鈕溪北?
  2. 點擊修改名字按鈕?

很顯然在進(jìn)行DOM相關(guān)操作(如setState)后夺脾,都會觸發(fā)getProductName函數(shù)之拨,但是我們想知道這個產(chǎn)品的名字,產(chǎn)品的價格怎么變不是我們關(guān)心的咧叭,所以我們需要讓這個函數(shù)只在產(chǎn)品名字改變的時候再觸發(fā)蚀乔,而不是每次重新渲染都觸發(fā)。

不控制重復(fù)渲染容易產(chǎn)生一些奇怪的問題

   // 假設(shè)在上面函數(shù)組件里面有一個定時任務(wù)
       setInterval(() => {
         console.log('觸發(fā)了定時器')
       }, 5000)

當(dāng)你點擊修改價格或者修改名字的時候菲茬,每次都會觸發(fā)渲染吉挣,說白了就是將這個函數(shù)組件的函數(shù),再執(zhí)行一次生均,顯然又會添加一個定時器听想, 這時就容易產(chǎn)生內(nèi)存泄漏,當(dāng)然你也可以在組件外部定義一個變量保存定時器id

2. 使用什么方法解決

2.1 useEffect马胧?

最開始遇到這個問題時汉买,由于我剛接觸hook,我認(rèn)為使用useEffect就能解決

    // 將上面的函數(shù)用useEffect包裹,并設(shè)置依賴佩脊,只有name發(fā)生變化的時候才觸發(fā)
     useEffect(() => {
        getProductName()
    }, [name])

我的想法很簡單蛙粘,就是讓getProductName只對name有效果垫卤,只有name修改的時候才會執(zhí)行。
顯然這是一個錯誤的認(rèn)識出牧,我只理解了useEffect可以設(shè)置依賴穴肘,并沒有理解到副作用到底是什么東西


看下面一段代碼

import React, {Fragment} from 'react'
import { useState, useEffect, useCallback, useMemo } from 'react'
import { observer } from 'mobx-react'


const nameList = ['apple', 'peer', 'banana', 'lemon']
const Example = observer((props) => {
    const [price, setPrice] = useState(0)
    const [name, setName] = useState('apple')
    
    
    function getProductName() {
        console.log('getProductName觸發(fā)')
        return name
    }
    // 只對name響應(yīng)
    useEffect(() => {
        console.log('name effect 觸發(fā)')
        getProductName()
    }, [name])
    
    // 只對price響應(yīng)
    useEffect(() => {
        console.log('price effect 觸發(fā)')
    }, [price])

    return (
        <Fragment>
            <p>{name}</p>
            <p>{price}</p>
            <p>{getProductName()}</p>
            <button onClick={() => setPrice(price+1)}>價錢+1</button>
            <button onClick={() => setName(nameList[Math.random() * nameList.length << 0])}>修改名字</button>
        </Fragment>
    )
})
export default Example

  1. 先看看useEffect的工作順序,若點擊修改名字按鈕會打印什么舔痕?
    > getProductName觸發(fā) 
    > name effect 觸發(fā)
    > getProductName觸發(fā) 

官方文檔有說過 當(dāng)你調(diào)用 useEffect 時评抚,就是在告訴 React 在完成對 DOM 的更改后運(yùn)行你的“副作用”函數(shù)

所以這個順序很好理解

  • 因為修改了名字,然后react更改了DOM伯复,觸發(fā)了getProductName
  • 隨后調(diào)用了nameeffect(在dom更新之后觸發(fā)慨代,這也是為什么叫做副作用)
  • effect中調(diào)用了getProductName
  1. 看看問題解決沒有,若點擊價錢+1按鈕會打印什么啸如?
    > getProductName觸發(fā) 
    > price effect 觸發(fā)

我改變的是價格侍匙,還是觸發(fā)了getProductName

稍微分析:

  • 顯然當(dāng)我使用setPrice的時候,產(chǎn)生DOM操作叮雳,刷新頁面DOM的同時也想暗,觸發(fā)了在p標(biāo)簽中的getProductName函數(shù)
  • 然后調(diào)用副作用觸發(fā)了priceeffect

就如前面我所提出的問題,我們的目標(biāo)是在DOM發(fā)生變化時帘不,不相關(guān)的函數(shù)不需要觸發(fā)(也就是這里的getProductName在我修改價格的時候不應(yīng)該觸發(fā))说莫,而useEffect只能在DOM更新后再觸發(fā)再去控制,所以這個馬后炮并沒有什么??用

2.2 useMemo厌均?

使用useMemo可以解決這個問題
為什么useMemo可以解決唬滑?官方文檔說過傳入 useMemo 的函數(shù)會在渲染期間執(zhí)行告唆,所以使用useMemo就能解決之前的問題棺弊,怎么在DOM改變的時候,控制某些函數(shù)不被觸發(fā)擒悬。
useMemo相近的還有一個useCallback模她,只是后者返回一個函數(shù)useCallback(fn, deps) 相當(dāng)于 useMemo(() => fn, deps),這里我們返回一個memo函數(shù)懂牧,
在上面的代碼中加入useMemo作對比

import React, {Fragment} from 'react'
import { useState, useEffect, useCallback, useMemo } from 'react'
import { observer } from 'mobx-react'


const nameList = ['apple', 'peer', 'banana', 'lemon']
const Example = observer((props) => {
    const [price, setPrice] = useState(0)
    const [name, setName] = useState('apple')
    
    
    function getProductName() {
        console.log('getProductName觸發(fā)')
        return name
    }
    // 只對name響應(yīng)
    useEffect(() => {
        console.log('name effect 觸發(fā)')
        getProductName()
    }, [name])
    
    // 只對price響應(yīng)
    useEffect(() => {
        console.log('price effect 觸發(fā)')
    }, [price])
  
    // memo化的getProductName函數(shù)   ??????
    const memo_getProductName = useMemo(() => {
        console.log('name memo 觸發(fā)')
        return () => name  // 返回一個函數(shù)
    }, [name])

    return (
        <Fragment>
            <p>{name}</p>
            <p>{price}</p>
            <p>普通的name:{getProductName()}</p>
            <p>memo化的:{memo_getProductName ()}</p>
            <button onClick={() => setPrice(price+1)}>價錢+1</button>
            <button onClick={() => setName(nameList[Math.random() * nameList.length << 0])}>修改名字</button>
        </Fragment>
    )
})
export default Example

同樣兩個問題

  1. 點擊價錢+1按鈕會發(fā)生什么
> getProductName觸發(fā)
> price effect 觸發(fā)
  • 首先DOM改變侈净,觸發(fā)在p標(biāo)簽中的getProductName函數(shù)
  • 然后調(diào)用effect

顯然我們已經(jīng)成功的控制了觸發(fā)(修改了顯示pricedom,但是沒有觸發(fā)memo_getProductName僧凤,沒有輸出''name memo 觸發(fā)'')畜侦,
這也是官方為什么說不能在useMemo中操作DOM之類的副作用操作,不要在這個函數(shù)內(nèi)部執(zhí)行與渲染無關(guān)的操作躯保,諸如副作用這類的操作屬于 useEffect 的適用范疇旋膳,而不是 useMemo,你可以試一下途事,在useMemo中使用setState你會發(fā)現(xiàn)會產(chǎn)生死循環(huán)验懊,并且會有警告擅羞,因為useMemo是在渲染中進(jìn)行的,你在其中操作DOM后义图,又會導(dǎo)致memo觸發(fā)

  1. 點擊修改名字按鈕會發(fā)生什么
> name memo 觸發(fā)
> getProductName觸發(fā)
> name effect 觸發(fā)
> getProductName觸發(fā)
  • 首先DOM變化减俏,觸發(fā)namememo
  • 然后觸發(fā)p標(biāo)簽內(nèi)的getProductName函數(shù)
  • DOM操作結(jié)束后觸發(fā)nameeffect
  • nameeffect中觸發(fā)getProductName

從這里也可以看出碱工,memo是在DOM更新前觸發(fā)的娃承,就像官方所說的,類比生命周期就是shouldComponentUpdate

3. 小結(jié)和補(bǔ)充

還有 7% 的精彩內(nèi)容
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載怕篷,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者草慧。
支付 ¥1.00 繼續(xù)閱讀
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市匙头,隨后出現(xiàn)的幾起案子漫谷,更是在濱河造成了極大的恐慌,老刑警劉巖蹂析,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铛绰,死亡現(xiàn)場離奇詭異,居然都是意外死亡退腥,警方通過查閱死者的電腦和手機(jī)蔗衡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝙叛,“玉大人俺祠,你說我怎么就攤上這事〗枇保” “怎么了蜘渣?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長肺然。 經(jīng)常有香客問我蔫缸,道長,這世上最難降的妖魔是什么际起? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任拾碌,我火速辦了婚禮,結(jié)果婚禮上街望,老公的妹妹穿的比我還像新娘校翔。我一直安慰自己,他們只是感情好灾前,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布防症。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪告希。 梳的紋絲不亂的頭發(fā)上扑浸,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機(jī)與錄音燕偶,去河邊找鬼喝噪。 笑死,一個胖子當(dāng)著我的面吹牛指么,可吹牛的內(nèi)容都是我干的酝惧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼伯诬,長吁一口氣:“原來是場噩夢啊……” “哼晚唇!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起盗似,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤哩陕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后赫舒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悍及,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年接癌,在試婚紗的時候發(fā)現(xiàn)自己被綠了心赶。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡缺猛,死狀恐怖缨叫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情荔燎,我是刑警寧澤耻姥,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站湖雹,受9級特大地震影響咏闪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜摔吏,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纵装。 院中可真熱鬧征讲,春花似錦、人聲如沸橡娄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挽唉。三九已至滤祖,卻和暖如春筷狼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背匠童。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工埂材, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人汤求。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓俏险,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扬绪。 傳聞我的和親對象是個殘疾皇子竖独,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一、react新特性 1. context 在一個典型的 React 應(yīng)用中挤牛,數(shù)據(jù)是通過 props 屬性自上而下...
    zxhnext閱讀 1,034評論 0 0
  • vue概述 在官方文檔中莹痢,有一句話對Vue的定位說的很明確:Vue.js 的核心是一個允許采用簡潔的模板語法來聲明...
    li4065閱讀 7,209評論 0 25
  • 1. 引言 工具型文章要跳讀,而文學(xué)經(jīng)典就要反復(fù)研讀墓赴。如果說 React 0.14 版本帶來的各種生命周期可以類比...
    黃子毅閱讀 2,075評論 0 10
  • 首先格二,讓我們談?wù)勈裁词莚eact hook。先剖出官方解釋: Hook 是 React 16.8 的新增特性竣蹦。它可...
    趣談前端_徐小夕閱讀 25,048評論 0 17
  • Web前端技術(shù)由 html顶猜、css 和 javascript 三大部分構(gòu)成,是一個龐大而復(fù)雜的技術(shù)體系痘括,其復(fù)雜程度...
    hnscdg閱讀 648評論 0 0