LoginSignup
0
0

【React】非同期処理が終わるまで処理中の表示を出すカスタムフック

Last updated at Posted at 2024-02-19

ここ1ヶ月ぐらい、Electronを使って自分用のMarkdownエディタを作っているのですが、ファイルシステム関連などの非同期処理中に画面操作を制限したい場面が結構出てきたので、カスタムフックにしてみました。

  • 既存のHook集に似たようなのがあるかも(未調査)ですが、自分でやってみるのもいいかなと…
  • この記事ではスタイリングにTailwindCSSを使っているので、他のライブラリと使う場合はうまいことやってください

useWaitProcessingフック

import { useState, useEffect } from 'react';

export default function useWaitProcessing() {
  const [promise, setPromise] = useState<Promise<unknown> | null>(null);
  const [isOpen, setIsOpen] = useState(false);

  useEffect(() => {
    if (promise === null) {
      setIsOpen(false);
      return;
    }
    //結果はどうあれ、Promiseが終わったら非表示にする
    promise.finally(() => setIsOpen(false));
  }, [promise]);

  // Promiseの処理中に表示するコンポーネント
  const WaitingScreen = () => (
    <div
      className={`fixed inset-0 z-[2147483637] flex items-center justify-center bg-slate-800/80 ${
        isOpen ? 'block' : 'hidden'
      }`}
    >
      <div className="size-10 animate-spin rounded-full border-b-4 border-l-4  border-slate-200" />
    </div>
  );

  return {
    // Promiseを受け取ったら表示して終わるのを待つ
    waitProcessing: (promise: Promise<unknown>) => {
      setIsOpen(true);
      setPromise(promise);
    },
    WaitingScreen,
  };
}

使う側の例

  • 以下の例では、Do somethingボタンを押すと、2秒かかる処理が発火するようになっています
  • heavyProcessが終わるまで処理中を示す表示になり、ボタンは押せなくなります
import useWaitProcessing from './hooks/useWaitProcessing';

export const App = () => {
  const { waitProcessing, WaitingScreen } = useWaitProcessing();

  const handleClick = (event: React.MouseEvent) => {
    // 非同期処理(2秒待つ)
    const heavyProcess = new Promise((resolve, reject) =>
      setTimeout(resolve, 2000)
    );
    // waitProcessingにPromiseを渡すと、終わるまで処理待ち画面になる
    waitProcessing(heavyProcess);
  };

  return (
    <div className="p-4">
      {/* WaitingScreenコンポーネントは適当なところに差しておく */}
      <WaitingScreen />
      <h1 className="font-bold">Hello!</h1>
      <button
        className="rounded-xl bg-slate-200 p-2 m-2 border border-slate-400 shadow-xl"
        onClick={handleClick}
      >
        Do something
      </button>
      <button
        className="rounded-xl bg-slate-200 p-2 m-2 border border-slate-400 shadow-xl"
        onClick={() => alert('Hello!')}
      >
        Show Message
      </button>
    </div>
  );
};

Stackblitzで動かしてみる

終わりに

Electronのメインプロセスで実行される処理の待ち用に実装したので、Promiseを渡すだけのお手軽実装にしました。あんまり融通は効きませんが、これで十分なシーンは結構あると思います。

  • 単にスピナーを出すだけなので、あまりにも長い処理には向かなそう。せいぜい2〜3秒の処理とかでしょうか
  • UIのレンダリングにかかわるデータ取得の待ちには向かなそうです。データ出力とか更新処理とか、そういうシーンで使うのが良さそう
  • これを使って、Electronのメインプロセスからレンダラープロセスを止める話も別で書きます
0
0
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
0
0