前一篇 useEffect 大概了解之後,我們這篇來簡單了解一下 dependecies (就是辣個 []
) 以及 useCallback 吧!
前一篇:https://codesandbox.io/s/03-02-useeffect-jkvtb9
本篇:https://codesandbox.io/s/04-usecallback-5hjnnp?file=/src/Score.js
(突然想到多拉A夢也是藍色的)
前一篇我們知道,useEffect 中提供的 dependecies(依賴) 來判斷需不需要執行。當我們不論是更改 minScore
或是執行 fetchData()
,這個 component 都會重新執行,我們可以放一組 Math.random()
來看看輸出,不論更改分數或重新抓取資料,輸出的 random
每次都不一樣,除此之外,我們的在裡面所建立的 function 也都不一樣!
const random = Math.random()
console.log(random) //每次都不一樣
//我也不會都是同一個 function
const fetchData = async () => {
setIsPending(true);
setList([]);
const data = await getPlayerList({ minScore });
setList(data);
setIsPending(false);
};
上一次,我們透過更改 minScore
來使 useEffect
重新取得資料,而 React 是使用 Object.is 來比對[]
裡面的值有沒有更新。
Object.is 可以想像成進階版的
===
還記得前一篇的 DEMO 中出現的黃色毛毛蟲嗎? (還是綠色?)
這時候我們可以先嘗試依照 eslint 的提示把 fetchData
也加入 []
:
React.useEffect(() => {
//略
}, [minScore, fetchData]);
很好,現在開始進行無限的 loop 了 (╬゚д゚)
回到先前講述到 Object.is ,fetchData 裡面包含了 state(ex: list, isPending)
的控制,而 fetchData
每次render 都會是不同的 instance,放入到 []
中自然比對上就會是不一樣的了,也同時形成了 loop,這時候有兩種做法:
useCallback
fetchData()
放到 useEffect 裡面const memorizedFunction = useCallback(() => {
//...
}, [])
or
const func = () => {...}
const memorizedFunction = useCallback(func, [])
這邊的
[]
與useEffect
的[]
是一樣的功能
我們就照著這樣的語法給他~套起乃!
const fetchData = React.useCallback(async () => {
setIsPending(true);
setList([]);
const data = await getPlayerList({ minScore });
setList(data);
setIsPending(false);
},[]);
這樣一來就不會 loop 了!
不過這樣也換這個 function 出現毛毛蟲啦! (怒)
useCallback
把這個 fucntion 記起來,但現在的 fetchData()
不論怎麼取資料,由於已經被記憶住了,因此 minScore
在這個 function 裡面的值會永遠是 0,其他的呢? setState
有 React 保障,不論如何都會是同一個 function ; 而我們的 getPlayerList()
也是在 component 外引進來; data
本身只存活在這個 scope,這樣一來……,就是 minScore
了!
比照辦理,我們也可以把 minScore
進一步加進[]
:
const fetchData = React.useCallback(async () => {
//略
},[minScore]); //加入minScore
我們也可以把 minScore
改以參數的方式傳進來(如下),就不用擔心記住的問題, fetchData()
也可以一直保持著同一個 function:
const fetchData = React.useCallback(async (minScore) => {
//前略
const data = await getPlayerList({ minScore });
//後略
},[]);
useEffect(()=> {
//..前略
fetchData(minScore)
//..後略
},[minScore])
(請用台語念這個標題)
若不使用 useCallback
,也可以把 fetchData()
移駕到 useEffect
裡面使用:
React.useEffect(() => {
//移駕近來
const fetchData = async () => {
//略
};
const timeout = setTimeout(() => {
console.log("fetching...");
fetchData();
}, 1000);
return () => clearTimeout(timeout);
}, [minScore]);
運作下來也沒問題,太讚啦!
「看起來 useCallback 真的很方便,那就把所有 function 都套上去使用吧!」
當然是不行,useCallback 雖然能提升效能,幫我們記憶,但這個動作並不是沒有代價的,最好的比喻就是:記憶吐司可以一直吃的嗎?
(然後大雄就拉肚子了)
那什麼時候會用到呢?當把 function 透過 props 傳遞,需要 reference equality:
function Foo({bar}) {
React.useEffect(() => {
const options = { bar }
buzz(options)
}, [bar])
return <div>foobar</div>
}
function Blub() {
const bar = React.useCallback(() => {}, [])
return <Foo bar={bar} />
}
可以看到 buzz 接受了一個 object,其中的 bar 是一個 function,而 bar 透過 prop 被傳遞進來,當 useEffect 知道 bar 變更時,就會執行。
這樣的情境下就能確保 bar 的傳遞時仍是一樣的 function,而當變更時能就進一步有不同的處理。
若本系列有錯誤的內容,歡迎隨時跟我告知。