iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 7
1
Modern Web

TypeScript + React + 雜七雜八系列 第 7

【Day 07】React Hooks,useRef 與 useMemo 與 useCallback

大家好,今天的篇章要介紹的是 useRef,useMemo,useCallback

會延續前一天的專案結構接續下去進行修改,如果沒有參與到昨天的建置過程,這邊也有提供原始碼
https://github.com/littlehorseboy/typescript-react/tree/day06-custom-hook


useRef

存放可變的值

這個 Hook 可以存放可變的值,在 【Day 05】React Hooks,useState 與 useEffect 的 component GetUUIDByMultipleOfFive.tsx 裡面已經有使用過,跟使用 useState 的改變值區別在於,它不會導致 component rerender

筆者認為使用的感覺就有點像是單純在

let something = 0;

function Component() {
  something += 1;
}

function 外宣告 let 的變數來存放值,只是是用這個 Hook useRef 來幫你管理

接下來新增一個 component 實際使用看看

+ src/components/day07/ConsoleRefNumber/ConsoleRefNumber.tsx

執行結果,這邊看看 console 的值,setInterval 不斷的累加值,不過可以觀察到畫面並不會被 rerender


存放 DOM

上述的存放可變的值,算是特別的情形才會用到,再來是 useRef 常見的應用,也就是為了操作 DOM,只要在 JSX 之中添加特殊的屬性 ref="[name]",直接展示程式碼

+ src/components/day07/InputFocus/InputFocus.tsx

居然還有紅色底線,這樣 webpack 編譯是不會過的

原來是因為這樣的寫法會讓 typescript 認為 useRef 是專門放 null 的

這時候就要請出 typescript 的泛型 <> 出場了,Hook 之中像是 useState、useRef,它們都可以設置泛型要使用什麼型別,如果沒設定,預設上它會認為是第一次指定的值的型別,所以這邊的程式碼一開始給了它 null,他就再也不會認為是 null 以外的型別了,更何況是要放 input 來進行操作,所以我們要讓他能知道自己除了 null 型別還能有 HTMLInputElement 的型別可以存取。

修改了第 4 行的 useRef<null | HTMLInputElement>,這樣設置泛型後,他就能認得自己除了 null 也可能會存進 HTMLInputElement 的東西。

然後修改第 7 行,用 if 判斷它只要不是 null,就能存取 HTMLInputElement 相關的函式。

執行結果

input 的 DOM 透過 ref 存進 inputRef,然後進行 .focus() 的操作就完成了。

typescript 的型別系統就是這樣讓你明瞭自己寫的程式碼,因為你如果不明白自己在寫的程式碼是什麼樣的型別,ts-loader 也不會讓你通過編譯的。


useMemo

簡言之,這是為了在 rerender 避免一些不必要的複雜計算而使用的,通常是優化效能階段或是一些特殊需求才會用到,先展示一般情形下 rerender 的情形

+ src/components/day07/CalculatePlus/CalculatePlus.tsx

執行結果可以看出,setFirstNumber() 或是 setSecondNumber() 在執行後都會讓 secondNumberPlusRandom 因為 rerender 重新計算新的值,筆者希望他僅僅只有在 secondNumber 被改變時,才去做計算,這時 useMemo 就可以派上用場了。

修改程式碼使用 useMemo

useMemo 的第一個參數定義運算的結果回傳,然後看到第二個參數是個陣列,其實就是跟 useEffect 的第二個參數一樣,監聽某個變數,變數有改變才會執行內部函式,

執行結果

最後就能看到只有 setSecondNumber() 去改變 secondNumber 後,useMemo 內的計算才會再次執行,因為筆者用了 Math.random() 來當計算值,所以這個示範有點像是特殊需求。

效能優化的情境像是取到的物件陣列去做 filter、map 等等的這種,亦可以利用 useMemo 來優化,減少整理或是處理陣列的次數。

筆者算是沒有真正處理過需要效能優化的情境,如果是更複雜到明顯有感延遲的運算,相信這個 useMemo 日後定能派上用場,就當作先記起來放囉。


useCallback

與前述的 useMemo 類似,是針對 function 來優化的手段,不過在 React 的官方說明還有提到 useCallback(fn, deps) 等同於 useMemo(() => fn, deps),useMemo 也可以做得到記憶 function,不過筆者認為應該是要用良好的命名來區隔比較好,就直接展示一段程式碼

+ src/components/day07/CalculatePlusFunc/CalculatePlusFunc.tsx

程式裡定義了 button 事件會 console 兩個數字相加

執行結果中可以看到,useCallback 將 handleClickSecondNumberPlusRandom 給記憶住了,所以 console 出來的值都會是 0,除非第二個參數 secondNumber 改變


最後附上原始碼
https://github.com/littlehorseboy/typescript-react/tree/day07-useref-usememo-usecallback


明天會介紹 Material-UI,它是一個在 github 上有五萬顆星星的 React UI Components Library


上一篇
【Day 06】React Hooks,custom Hook
下一篇
【Day 08】React UI Components Library 「Material-UI」
系列文
TypeScript + React + 雜七雜八30

尚未有邦友留言

立即登入留言