input タグの onChange
イベントとか、入力の度に state 変更して再レンダリングするのなんだかなーと思ってたので、イベントの処理を間引いて実行したいと思ったので色々調べながら作ってみました。
要件としては、イベントが発生した後に同じイベントが発生しなかったらイベントで渡された値を使った処理を実行したい。という感じ
できたもの
See the Pen Debounce Hook by KIKIKI (@kikiki_kiki) on CodePen.
debounce と throttle
処理を間引く方法は debounce
と throttle
という方法があるっぽい。
違いがこんな感じ
debounce
指定時間内に何度イベントが発生しても、最後の1回だけが実行される。
指定時間内にイベントが発生すると、先のイベントをキャンセルして最後のイベントからまた指定時間待って、その間にイベントが発生しなければ処理を実行する感じ。
throttle
一定時間内に1回だけイベントを実行させる。
時間を区切って、指定時間内に何度イベントが発生しても最後の1回だけを実行する。インターバルのようなイメージ。
今回作成した Hook は input タグの入力を間引きたいようなイメージだったので、入力が終わったら処理を実行したい = 入力中に発生する onChange
を間引いて最後の onChange
イベントだけ拾いたい。という方向だったので debounce
を採用しました。
コード👇
useDebounceCallback Hook
import { useState, useCallback, useRef } from 'react'; const useDebounceCallback = (callback, delay = 300) => { const [val, setVal] = useState(); const decounceTimer = useRef(null); const dispatch = useCallback((_val) => { clearTimeout(decounceTimer.current); decounceTimer.current = setTimeout(() => { setVal( callback(_val) ); }, delay); }, [callback, delay, decounceTimer]); return [val, dispatch]; }; export default useDebounceCallback;
使い方
import React, { useCallback } from 'react'; import useDebounceCallback from './useDebounceCallback.js'; function MyComponent() { const double = useCallback((val) => { return val * 2; }, []); const [doubleValue, setDoubleValue] = useDebounceCallback(double, 1000); const onChangeHandler = (e) => { setDoubleValue(e.target.value - 0) }; return ( <input type="number" onChange={onChangeHandler} /> <span>Double: {doubleValue}</span> ); }
通常の実行したい関数と、間引きたい時間を渡すと、state
と 値をセットする関数を呼び出す despatch
が返ってくるので、後はいつもの useState
のような感じで state
の更新をすればOKという感じ。
useRef
こんな使い方も出来るんだ〜と知れたので楽しかった。
雑に作ってあるので callback
が関数でない時とか、useCallback
の中で setTimeout
するの実際どうなん?とか抜けや理解しきれてない部分があるけど、なんとなくいい感じに動作してる気がしてるので、今はこれが精一杯… という感じでよしとしています。
もっとこうした方が良いとかあれば教えて下さい!
[参考]