發現錯誤 請勿參考本文內容 待後續修改
終於把 debounce 的功能加到搜尋欄裡面了!當初一度要放棄,先用按鈕做代替。後來突破盲腸,終於做出有 autocomplete 的搜尋列。在 react 裡面實作這項功能對我這個萌新而言,比較複雜。而且很多作法是從來沒想過的。
第一個沒想到的事,我以為會直接用 useState 去控制輸入進搜尋列的值。像這樣:
const [inputValue, setInputValue] = useState("")
<input value={inputValue} onChange={setInputValue} />
但實際根據 Clay 助教的寫法會是這樣:
let input = useRef("")
const [inputText, setInputText] = useState("")
<input ref={input} onChange={() => {
setInputText(input.current.value)
}}
我蠻好奇這種寫法除了 debounce 以外的其他使用時機。至於為什麼要這樣寫? 看到後面應該就會知道了。
最後再配上這個:
useEffect(() => {
callapi()
},[inputText])
就可以在 inputText 改變的時候去 callapi。
這樣也可以跑,但就是每次輸入改變的時候,都會 call api。我們當然不希望這樣(尤其當你是 google api 的免費仔,call 的數量有限)。
在這之前還是先試試看 react 裡面可不可以好好 debounce。我去載了 lodash 這個套件,裡面有 debounce 這個方法讓我們輕鬆 debounce。
import _ from "lodash";
const debounced = _.debounce(??,1000)
這時候我卡住了,我一開始去 debounce callapi 的部份,很直覺的想法,因為我只想送出一個 request。但這樣有兩個問題。
第一個 react 裡面用 debounce 要用 useCallback 這個 hook。原因的話,我自己的理解是,每次 react 渲染頁面都會重新呼叫一次函式。那我們在函式裡面的
const debounced = _.debounce(??)
也會被重新宣告一次。那這樣上一次呼叫的debounced
和這次呼叫的debounced
就不會是同個函式。 如果大概了解過 debounce 就知道。我們應該是呼叫同個函式多次才會得到 debounce 的效果。所以變成這樣:const debounced = useCallback(_.debounce(??,1000),??)
useCallback 的第二個參數是,當這個參數改變的時候,會回傳不同的函式。當初我耍蠢放進
[inputText]
,然後就沒有 debounce 了。這裡應該會希望不管怎樣都會傳同樣的函式,所以放[]
:const debounced = useCallback(_.debounce(??,1000),[])
第二個問題是要 debounce 什麼,前面説到是這樣
const debounced = useCallback(_.debounce(callapi(),1000),[])
看起來合理,等一秒沒打字再 callapi。那要什麼時候呼叫 debounced? 前面的 useEffect 的 callapi 變成 debounced。所以是 inputText 改變的時候,就呼叫 debounced。
useEffect(() => { debounced() },[inputText])
但我用了之後發現有個問題。debounced 回傳的函式都是一樣的。「咦?前面才說要一樣啊」一樣的話代表 callapi() 裡面的參數也是一樣的。我們這次要做的是 autocomplete 的效果。所以隨著 inputText 改變,callapi 裡面的參數也要改變(要戴上使用者輸入去搜尋)。但現在放進 useCallback 裡面我不會改了。輾轉到最後,就用 Clay 教學裡面的方法。
就是 debounce 的不是 callapi ,是 setInputText
let input = useRef("")
import _ from "lodash";
const [inputText, setInputText] = useState("")
<input ref = {input} onChange={() => {
debounced()
}}/>
const debounced = useCallback(_.debounce(setInputText(input.current.value),1000),[])
useEffect(() => {
callAPI()
},[inputText])
每次 input 的值改變的時候都會呼叫 debounced ,這時候真正被 debounce 的是 setInputText。所以在打字停止一秒這之前,inputText 都維持初始值,沒有改變。停止打字一秒後,inputText 才真正被改變。而被改變時,也觸發 useEffect,所以去 callapi。真是漂亮的設計!
結果長這樣:
有任何錯誤歡迎指正,感謝觀賞。