是否推薦大量使用useCallback和useMemo坤邪?

前言

剛開始使用react hooks熙含,大量使用useCallback,認(rèn)為是性能優(yōu)化處理艇纺;后來發(fā)現(xiàn)有些場(chǎng)景使用useCallback反而造成性能問題怎静。認(rèn)真了解相關(guān)資料后邮弹,輸出以下總結(jié)。

結(jié)論在前

官方文檔不推薦大量使用蚓聘。

推薦使用原則:

不懂的不用腌乡;懂的少用。最好是遇到需要性能優(yōu)化時(shí)再去處理夜牡。

理由:
  • react re-render的渲染性能很高与纽,大部分時(shí)候足以包容大部分web項(xiàng)目出現(xiàn)的hooks重復(fù)渲染引起的性能消耗。主要因?yàn)閞e-render作用于vertual dom塘装,而不是實(shí)際的dom急迂,所以re-render的渲染性能很高。
  • 相對(duì)于花費(fèi)額外的開發(fā)成本關(guān)注細(xì)微的性能優(yōu)化蹦肴,更推薦減少訂閱視圖無關(guān)的數(shù)據(jù)僚碎,以避免重復(fù)渲染。
  • 從react hooks設(shè)計(jì)上來說阴幌,本身就接受re-render是常態(tài)的事實(shí)勺阐,否則就應(yīng)該把useCallback和useMemo放在架構(gòu)里使用,而不是手動(dòng)調(diào)用矛双。大量使用useCallback和useMemo渊抽,反而違背了最小知識(shí)原則和接口隔離原則
  • useCallback的設(shè)計(jì)初衷并非解決組件內(nèi)部函數(shù)多次創(chuàng)建的問題,而是減少子組件的不必要重復(fù)渲染

useCallback和useMemo

相同點(diǎn)

通過緩存函數(shù)(對(duì)象)背零,減少re-render腰吟。僅依賴項(xiàng)發(fā)生變化時(shí),才觸發(fā)re-render

不同點(diǎn)
  • useCallback緩存函數(shù)
  • useMemo緩存對(duì)象
useCallback(fn, deps)
useMemo(() => fn, deps)
作用

在react hooks中徙瓶,由于state更新或父組件更新等原因都容易引起的組件re-render毛雇,都會(huì)導(dǎo)致函數(shù)和對(duì)象的重復(fù)聲明和計(jì)算。useCallback和useMemo通過緩存函數(shù)或?qū)ο笳煺颍_(dá)成僅初始化或依賴項(xiàng)發(fā)生變化時(shí)灵疮,才會(huì)觸發(fā)對(duì)應(yīng)函數(shù)或?qū)ο蟮膔e-render的作用。

Re-render

在react class對(duì)應(yīng)的生命周期shouldComponentUpdate可以檢測(cè)到組件內(nèi)部的部分更新壳繁;同樣的react hooks中為了可以檢測(cè)到狀態(tài)的更新震捣,引入了re-render。

對(duì)于函數(shù)組件的 re-render闹炉,大致分為以下三種情況:

  • 組件本身使用 useState 或 useReducer 更新蒿赢,引起的 re-render;
  • 父組件更新引起的 re-render渣触;
  • 組件本身使用了 useContext羡棵,context 更新引起的 re-render。
    三種情況場(chǎng)景

性能優(yōu)化

常規(guī)使用

count每次更新時(shí)嗅钻,都會(huì)導(dǎo)致Hello組件的渲染皂冰,對(duì)于Hello組件來說店展,這是無效的更新

const Hello = ({ name }) => {
  console.log("hello render");
  return<div>hello {name}</div>;
};
const App = () => {
  console.log("app render");
  const [count, addCount] = useState(0);
  return (
    <div className="app">
      <Hello name="react" />
      <div className="counter-num">{count}</div>
      <button
        onClick={() => {
          addCount(count + 1);
        }}
      >
        add
      </button>
    </div>
  );
};
優(yōu)化方案
  • 將更新部分抽離成單獨(dú)組件
// Counter組件內(nèi)部的渲染觸發(fā)Hello進(jìn)行無效的渲染
const App = () => {
  return (
    <div className="app">
      <Hello name="react" />
      <Counter />
    </div>
  );
};
  • 將不需要 re-render 的部分抽離,以插槽形式渲染(children)
// App 組件預(yù)留 children 位秃流, count數(shù)據(jù)更新不會(huì)觸發(fā)children的更新
const App = ({ children }) => {
  const [count, addCount] = useState(0);
  return (
    <div className="app">
        {children}
      <div className="counter-num">{count}</div>
      <button
        onClick={() => {
          addCount(count + 1);
        }}
      >
        add
      </button>
    </div>
  );
};
// 使用
<App>
  <Hello name="react" />
</App>
  • React.memo
    問題:Hello組件props沒有更新赂蕴,但由于父組件count的更新,觸發(fā)re-render
    解決辦法:
// Hello組件memo化舶胀,count數(shù)據(jù)變化時(shí)概说,Hello 組件是不會(huì)re-render的。除非Hello組件的props更新峻贮。
const Hello = React.memo(({ name }) => {
  console.log("hello render");
  return<div>hello {name}</div>;
});
  • useCallback
    問題:count變化席怪,引起clickHandler重新計(jì)算;onClick更新纤控,又引起Hello組件更新
// 新增 onClick 處理函數(shù)
const Hello = React.memo(({ name, onClick }) => {
  console.log("hello render");
  return<div onClick={onClick}>hello {name}</div>;
});

const App = ({ children }) => {
  console.log("counter render");
  const [count, addCount] = useState(0);
  
  // 新增處理函數(shù)
  const clickHandler = () => {
    console.log("hello click");
  };

  return (
    <div className="counter">
      <Hello name="react" onClick={clickHandler} />
      <div className="counter-num">{count}</div>
      <button
        onClick={() => {
          addCount(count + 1);
        }}
      >
        add
      </button>
    </div>
  );
};

解決辦法:

// 新增處理函數(shù)挂捻,使用 useCallback 緩存起來
const clickHandler = useCallback(() => {
 console.log("hello click");
}, []);

例外:useCallback無效場(chǎng)景

// 新增處理函數(shù),使用 useCallback 緩存起來
// 在 callback 函數(shù)中使用 count
// 并將 count 添加進(jìn)依賴
// 只要 count 更新船万,callback 函數(shù)又將更新刻撒,useCallback 就沒什么用了
const clickHandler = useCallback(() => {
 console.log("count: ", count);
}, [count]);

useCallback/useMemo 的使用誤區(qū)
產(chǎn)生誤區(qū)的原因是useCallback的設(shè)計(jì)初衷并非解決組件內(nèi)部函數(shù)多次創(chuàng)建的問題,而是減少子組件的不必要重復(fù)渲染

總結(jié)

1.不推薦大量使用useCallback和useMemo耿导,更推薦通過不訂閱視圖無關(guān)的數(shù)據(jù)声怔、細(xì)化組件粒度(將更新部分抽離成單獨(dú)組件、將不需要 re-render 的部分抽離舱呻,以插槽形式渲染)醋火,減少re-render影響范圍。
2.useCallback返回作為props的函數(shù)箱吕,需要搭配memo使用芥驳。

參考文獻(xiàn)

https://mp.weixin.qq.com/s/S9DNxUxtlbwRrwC_YEc26w
https://www.zhihu.com/question/390974405

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市茬高,隨后出現(xiàn)的幾起案子兆旬,更是在濱河造成了極大的恐慌,老刑警劉巖怎栽,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丽猬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡熏瞄,警方通過查閱死者的電腦和手機(jī)脚祟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來强饮,“玉大人愚铡,你說我怎么就攤上這事。” “怎么了沥寥?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)柠座。 經(jīng)常有香客問我邑雅,道長(zhǎng),這世上最難降的妖魔是什么妈经? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任淮野,我火速辦了婚禮,結(jié)果婚禮上吹泡,老公的妹妹穿的比我還像新娘骤星。我一直安慰自己,他們只是感情好爆哑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布洞难。 她就那樣靜靜地躺著,像睡著了一般揭朝。 火紅的嫁衣襯著肌膚如雪队贱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天潭袱,我揣著相機(jī)與錄音柱嫌,去河邊找鬼。 笑死屯换,一個(gè)胖子當(dāng)著我的面吹牛编丘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彤悔,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼嘉抓,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蜗巧?” 一聲冷哼從身側(cè)響起掌眠,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎幕屹,沒想到半個(gè)月后蓝丙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡望拖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年渺尘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片说敏。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鸥跟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情医咨,我是刑警寧澤枫匾,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站拟淮,受9級(jí)特大地震影響干茉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜很泊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一角虫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧委造,春花似錦戳鹅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至亮垫,卻和暖如春模软,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饮潦。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工燃异, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人继蜡。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓回俐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親稀并。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仅颇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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