LoginSignup
8
1

More than 1 year has passed since last update.

値が確定されてから実行される遅延評価useEffect

Last updated at Posted at 2020-06-20

株式会社ムラウチドットコムのフロントエンドエンジニアです。
ムラウチドットコムのOrganizationができて初めての投稿です。

今回はユーザーの入力で値が確定してから実行されるカスタムuseEffectを考えたので紹介します。

コード

import React from "react";

export const useDelayedEffect = (
  effect: React.EffectCallback,
  deps?: React.DependencyList,
  delaytime: number = 1000,
) => {
  const [waiting, setWaiting] = React.useState(false);
  const timer = React.useRef<number>();

  React.useEffect(() => {
    window.clearTimeout(timer.current);

    setWaiting(true);

    timer.current = window.setTimeout(() => {
      setWaiting(false);
    }, delaytime);
  }, deps);

  React.useEffect(() => {
    if (!waiting) {
      effect();
    }
  }, [waiting]);
};

使い方

普通のuseEffectの引数に加えて入力が確定したと判断するまでのdelaytimeを指定するだけです。

import React from "react";
import { useDelayedEffect } from "./useDelayedEffect";

export const App = () => {
  const [text, setText] = React.useState("");
  const [confirmedText, setConfirmedText] = React.useState("");

  useDelayedEffect(
    () => {
      setConfirmedText(text);
    },
    [text],
    1500,
  );

  return (
    <div>
      <input onChange={e => setText(e.target.value)} value={text} />
      <p>{`text: ${text}`}</p>
      <p>{`confirmedText: ${confirmedText}`}</p>
    </div>
  );
};

画面収録 2020-06-21 0.39.58.mov.gif

解説

入力完了を待っているかどうかの状態をwaitingで保持します。
ひとつめのuseEffectwaitingtrueにしてdelaytimeミリ秒後にwaitingfalseにするタイマーをセットします。
useEffectは入力1文字1文字で実行されるので、その都度タイマーを解除してdelaytimeを測り直しています。
レンダリングのたびにカスタムフック関数が実行されるため、

let timer;
// 中略
window.clearTimeout(timer);
// 中略
timer = window.setTimeout(() =>  setWaiting(false), delaytime);

では実装できないことに注意してください(毎回timerが初期化されるため)。useRefを用いることで再レンダリングされても参照できる変数を定義します。

ふたつめのuseEffectwaitingtrueからfalseになったとき、つまり入力がdelaytimeミリ秒以上行われなかったときに引数effect関数を実行します。

ユースケース

ユーザーの入力値を自動保存する場合などに利用できると思います。
通常のuseEffectに保存のためのHTTPリクエストを仕込んでおくと、例えば "Hello world." を入力すると12回HTTP送信してしまいます。
そうではなくユーザーの入力が終わってから送信すれば無駄なHTTPリクエストをへらすことができます。

まとめ

入力が確定してから実行されるuseEffectを紹介しました。
もっといい書き方やすでに実装されているライブラリ等あったらぜひおしえてください。

追記

この書き方で十分なことに気づいた!

export function useDelayedEffect(
  effect: React.EffectCallback,
  deps: React.DependencyList,
  timeout: number = 1000,
) {
  useEffect(() => {
    const timeoutId = setTimeout(effect, timeout);

    return () => clearTimeout(timeoutId);
  }, deps);
}
8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1