かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

React イベントを間引いて処理を実行できる Debounce な Hook 作ってみた。

input タグの onChange イベントとか、入力の度に state 変更して再レンダリングするのなんだかなーと思ってたので、イベントの処理を間引いて実行したいと思ったので色々調べながら作ってみました。

要件としては、イベントが発生した後に同じイベントが発生しなかったらイベントで渡された値を使った処理を実行したい。という感じ

できたもの

See the Pen Debounce Hook by KIKIKI (@kikiki_kiki) on CodePen.

debounce と throttle

処理を間引く方法は debouncethrottle という方法があるっぽい。
違いがこんな感じ

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 するの実際どうなん?とか抜けや理解しきれてない部分があるけど、なんとなくいい感じに動作してる気がしてるので、今はこれが精一杯… という感じでよしとしています。
もっとこうした方が良いとかあれば教えて下さい!


[参考]