序
ここ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のメインプロセスからレンダラープロセスを止める話も別で書きます