iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
0
Modern Web

關於React,那些我不知道的系列 第 3

來個聰明的傢伙,幫我記憶一些資料吧! 出來吧 useMemo (9/19 修正描述)

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20200919/20130721zSPDpBun1S.png
Why

除了React本身的渲染機制外,畫面被渲染出來前,也會執行一連串符合需求、我們自定義的運算,當元件重新渲染時,我們不希望某些數據被重算,這時我們希望這個數據可以被記憶起來


比如說 Hello world 常見的印星星 ******
我們根據某個變數 count 來決定要印幾顆星星,那今天這個 count 不變
假如這是一個很貴的運算,我們不希望他被重新運算,所以就會想把這個結果記憶起來
於是乎,我們就可以派出 useMemo 了。


     // 印星星
     let stars = "";
     for (let i = 1; i <= count; i += 1) {
          stars += "*";
     }

how

使用 React 強大的API,useMemo

Demo codesanbox Link
show me the code

 const result = useMemo(() => {
   let stars = "";
   for (let i = 1; i <= count; i += 1) {
     stars += "*";
   }
   return stars;
 }, [count]); //dep array 中的count,假設是元件內某個state 或props
 
 return (
     <div>
         <h1>{result}</h1>
     </div>
 )

今天的範例當中,一時間想不到什麼太複雜、昂貴的運算,就決定使用 Math.random() 來當例子啦!


一般情況下,已知只要有任何一個state / props改變,我們這個元件就會被重新渲染 (rerender)

情境如下

我們的元件中,有兩個state,其中有個state 跟亂數有關,另一個沒有。
我希望只有這個跟亂數有關的state 改變,畫面才會產生新的亂數。另一個狀態改變時,這個亂數在畫面上維持不變 (不會被重新渲染)。

  const [radomDepState, setRadomDepState] = useState(1);
  const [count, setCount] = useState(1);
  const random = ???????
  // ------------------------------------------------
  <h1>{random}</h1>

現在我們有兩個state,一個跟radom有關,另一個沒有,畫面上也有個random,這個random會長這樣:

 function createRandomNumber() {
    return parseInt(1000 * Math.random());
 }
 const random = createRandomNumber()

接著我們希望有些機制,可以改變 radomDepState / count 這兩個狀態
因此我們加入了兩個button,並且在畫面上顯示這兩個state。

        {count}
        <button onClick={() => setCount(count + 1)}>
          不相關的state
        </button>
        {radomDepState}
        <button onClick={() => setRadomDepState(radomDepState + 1)}>
          useMemo依賴的radomDepState
        </button>

此時,我們點任何一個按鈕,畫面上的亂數都會跳來跳去、不停地改變

接著來調整一下原本的random,思考一下useMemo 會被如何使用呢?

 const random = useMemo(doing something here);

9/19修正描述, 官網說 useMemo 吃兩個參數 useMemo( fn , dependencies ) 【React】官網是說:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

想當然爾,第一個fn computeExpensiveValue 當然就是我們昂貴計算的本人啦!本範例就是那個createRandomNumber了啊。 因為我們不希望亂數一直被改變。

而官網又說第二個參數是個Array,我們不管,就先帶入一個空陣列 [] 進去看看。

 const random = useMemo(() => createRandomNumber(), []);

此時我們發現,各自點擊按鈕時,畫面上的 count / radomDepState 都會跟著改變!
但唯獨~~~~~!!!!!!!! 我們的 random 自此致終不再改變過QwQ

這跟我們想要的情境不一樣啊~~~~

我希望是點影響radomDepState的那顆按鈕,我們就要產生新的亂數

9/19修正描述, useMemo( fn , dependencies ) 【React】官網是說:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

此時回頭看看,第二個dependencies,英文單字說文解字 依存關係 / 依屬 WTF~~

原來這就是那個,當有什麼東西發生改變時,我們要重新進行這個昂貴運算的依賴。
那些 state / props 也就是 我們所謂的依賴。
既然用了「那些」,代表這可以是一個複數群,因此我們的dependencies是用array儲存起來。

 //  const random = useMemo(() => createRandomNumber(), [radomDepState]);
 //  我們直接來個簡寫好了
 const random = useMemo(() => {
    return parseInt(1000 * Math.random());
  }, [radomDepState]);

此時,我們可以看到,當count改變時,我們的random不會變成其他隨機數字。但radomDepState改變時,我們就會產新的亂數!

Demo codesanbox Link


結論

1. useMemo 這個技巧,時常被作為 React App 優化效能的手段

如果你在 render 當中執行了昂貴的計算,你可以使用 useMemo 來最佳化。React 官網

2. 但官網也有提到,不保證全部用了都有效

你可以把 useMemo 作為效能最佳化的手段,但請不要把它當作成語意上的保證。 React 官網

意思是說,不是什麼東西丟到useMemo 就是效能優化,提醒我們不要無限上綱的亂用XD!

3. 使用時機:使用前,請詳閱公開說明書

這邊也要注意到,useMemo 在 React 當中,被執行的時機是在render當中,而不是render結束時。(不像是useEffect,是在畫面渲染完後執行)
所以我們要避免一些會阻礙畫面渲染的事情,比如說call API 之類的side Effect。

要謹記傳到 useMemo 的 function 會在 render 期間執行。不要做一些通常不會在 render 期間做的事情。例如,處理 side effect 屬於 useEffect,而不是 useMemo
React 官網

參考資料

React 性能優化那件大事,使用 memo、useCallback、useMemo
(https://medium.com/%E6%89%8B%E5%AF%AB%E7%AD%86%E8%A8%98/react-optimize-performance-using-memo-usecallback-usememo-a76b6b272df3)

Ya~~~~ 完成任務啦! 我指第三天的....

明天繼續努力


上一篇
關於 render 與 re-render,那些React不說,但默默幫我們做的事(內容比起React跟Observer Pattern 更有關)
下一篇
關於props的記憶,React Memo (新增範例及說明)
系列文
關於React,那些我不知道的30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言