おすすめ自作 React hooks 集 4
公式ドキュメントへのリンク
useMemoBuffered
useMemo
(→ フック API リファレンス#useMemo) でメモ化したい値が、頻繁に更新される他の値に依存しており、一定時間(200msとか)その更新が止むまで再計算を待ちたい、という状況がたまに存在します(たとえばinput要素の入力された文字列に基づきリストをフィルタリングしたいときなど)。
メモ化したい値が頻繁に更新される値に依存しているが再計算も毎回必要というわけではなく適宜間引きたいという状況では、次のようなcustom hooksが役に立ちます。(RxJSの debounceTime
オペレータのような動作です。)
import { useEffect, useRef, useState } from 'react';
export function useMemoBuffered<ResultValue>(
fn: () => ResultValue,
deps: any[],
bufferMilliSec: number
): ResultValue {
const ref = useRef<{ timerId: number | undefined }>({ timerId: undefined });
const [value, setValue] = useState<ResultValue>(fn);
useEffect(
() => {
// 前の予約をキャンセル
clearTimeout(ref.current.timerId);
// fnの実行をbufferMilliSecミリ秒後に予約
ref.current.timerId = setTimeout(() => {
setValue(fn());
}, bufferMilliSec);
},
deps // eslint-disable-line react-hooks/exhaustive-deps
);
return value;
}
使い方はほとんど useMemo
と同じです。
export const SomeComponent = () => {
const [name, setName] = useState<string>('');
// nameの値が更新されたとき、その更新が200ミリ秒間止むまで待ってからresultを再計算する。
const result = useMemoBuffered(() => computeExpensiveValue(name), [name], 200);
return (
<div>
<input value={name} onChange={event => setName(event.target.value)} />
<SomeChildComponent result={result} />
</div>
)
}
2020/03/06追記
useEffect
でクリーンアップを忘れていたためメモリリークの危険がありました。正しくは以下のように書き、useMemoBuffered
hooks が使われるcomponentが破棄されたときにtimerを破棄する必要があります。
import { useEffect, useRef, useState } from 'react';
export function useMemoBuffered<ResultValue>(
fn: () => ResultValue,
deps: any[],
bufferMilliSec: number
): ResultValue {
const ref = useRef<{ timerId: number | undefined }>({ timerId: undefined });
const [value, setValue] = useState<ResultValue>(fn);
const clearTimer = useCallback(() => {
clearTimeout(ref.current.timerId);
}, []);
useEffect(
() => {
// 前の予約をキャンセル
clearTimer()
// fnの実行をbufferMilliSecミリ秒後に予約
ref.current.timerId = setTimeout(() => {
setValue(fn());
}, bufferMilliSec);
return clearTimer; // ← これが必要
},
deps // eslint-disable-line react-hooks/exhaustive-deps
);
return value;
}
おすすめ自作 React hooks集は随時追加予定です。