今天要分享的是我们在优化前端时常会做的处理,Debounce & Throttle,那关于这两个名词的解释,网路上有很多专门在讲解的文章,也可以参考连结。
所以,以实现这样的功能来说我们会需要一个setTimeout来当作阻挡的中继器,我们可以用custom hook的概念做组建的拆分,用来实践两种不同情境的功能。
首先,我们先试者製作useTimeout的custom hook:
import { useCallback, useEffect, useRef } from "react";const useTimout = (callback, delay) => { const callbackRef = useRef(callback) const timeoutRef = useRef() // 透过 ref change 条件触发更新事件 useEffect(() => { callbackRef.current = callback }, [callback]) // 透过 useCallback 减少重複 setTimeout const set = useCallback(() => { timeoutRef.current = setTimeout(() => callbackRef.current(), delay) }, [delay]) const clear = useCallback(() => { timeoutRef.current && clearTimeout(timeoutRef.current) }, []) // 这里是主要的作用 hook 主要做值更改的部分,在更动完后清除timeout useEffect(() => { set() return clear }, [delay, set, clear]) const reset = useCallback(() => { clear() set() }, [clear, set]) // 如果不用 clear, reset function 也可以不定义 return { clear, reset }}export default useTimout;
这个hook就是一个计时器功能的作用,有clear & reset两个function可以做调整,使用範例:
// ...省略import useTimeout的做法const exampleComponent = () => { const [count, setCount] = useState(0) const {clear, reset} = useTimeout(() => setCount(10), 1000) return ( <div> <h1>useTimout</h1> <p>{count}</p> <Button p={`1rem`} onClick={() => setCount(prev => prev + 1)}>++</Button> <Button p={`1rem`} onClick={clear}>clear</Button> <Button p={`1rem`} onClick={reset}>reset</Button> </div> )}
跑起来后会在一秒之后自动执行() => setCount(10)
的function, 也可以当倒数计时器使用。
接着我们照这debounce的执行逻辑去製作useDebounce的custom hook:
import { useEffect } from "react"import useTimout from "./useTimeout"// 等待动作停止后,触发其他function,适合用于query apiconst useDebounce = (callback, delay, dependencies) => { // 这边直接将动作和delay时间带入useTimeout const { reset, clear } = useTimout(callback, delay) // dependencies是整个array, 当然你可以只带入里面的值 // 这里是透过reset重新计时 useEffect(reset, [...dependencies, reset]) // 这里不希望一开始就触发callback,所以加了一个clear useEffect(clear, [])}export default useDebounce;
使用範例如下:
// ...省略import hooksconst exampleComponent = () => { const [count, setCount] = useState(0) // 这里的alert只是示範,实务上可以改成其他动作的function useDebounce(() => alert(count), 1000, [count]) return ( <div> <h1>useDebounce</h1> <p>{count}</p> <Button p={`1rem`} onClick={() => setCount(prev => prev + 1)}>++</Button> </div> )}
设想一下这类的功能如果放在原本searchbar input的功能栏位,可以有效降低不必要的query,所以在实际应用上是很常见的功能。
那么,以上的範例就是这一篇的分享,Throttle的部分就让你们自己练习吧!
基本上逻辑是差不多的,这系列的更新可能就到这篇吧,因为有太多第三方的套件已经整合好这类custom hook的function,这也是为什么那么久没更新了,最近转而使用typescript在专案上,像usehook或是mantine这类的套件,能大幅降低自己写custom hook的时间,而且有团队维护应该还是比较省事的做法,一样推荐给各位,希望还是有帮助到大家!