ローディング状態の制御などの非同期処理では処理中であることを示す、try...catch
を使います。
さらにfinally
を併用することで、ローディングのような「必ず実行したい処理」を確実に実行することができたので、今回備忘録としてまとめます。
前提条件
- Reactを使ったコンポーネント内で状態管理をしている
- SupabaseなどのAPIから非同期でデータを取得・送信するケースを扱っている
- ローディング状態(
loading
)はuseState
で管理されている - 関数には
async
が使われており、非同期処理の完了をawait
で待っている
try...catch...finallyとは
try...catch
非同期処理やエラーが起こりうる処理をtry
ブロックに書きます。そこでエラーが発生すると、処理は中断され、catch
ブロックに移ります。
try {
// エラーが起こるかもしれない処理
} catch (error) {
// エラー時の処理
}
finally
finally
ブロックは、try
やcatch
のあとに必ず実行される部分です。たとえreturn
やthrow
があっても実行されます。処理の後片付けや状態のリセットに適しています。
try {
// 通常の処理
} catch (error) {
// エラー処理
} finally {
// 最後に必ず実行される処理
}
try...catch...finallyを使わないとどうなる
これはsetLoading(false)
を処理のたびに明示的に書くことで、どのパスを通ってもローディングが解除されるようにした例です。ただし、毎回書く必要があるため冗長的に感じたためtry...catch...finally
を使いました。
const handleAdd = async () => {
setLoading(true);
if (text.trim() === "") {
setLoading(false);
setErrorMessage("学習内容を入力してください");
return;
}
const newRecord = { ... };
const { data, error } = await supabase
.from("study-record")
.insert([newRecord])
.select();
if (error) {
setLoading(false);
setErrorMessage("データの保存に失敗しました");
return;
}
setRecords([...records, data[0]]);
setLoading(false);
};
try...catch...finallyで整理すると
ローディングの開始をtry
の前に、解除をfinally
に記述することで、記述を1箇所にまとめることができます。
const handleAdd = async () => {
setLoading(true);
try {
if (text.trim() === "") {
setErrorMessage("学習内容を入力してください");
return;
}
const newRecord = { ... };
const { data, error } = await supabase
.from("study-record")
.insert([newRecord])
.select();
if (!data) {
throw new Error("サーバーでエラーが発生しました");
}
if (error) {
setErrorMessage("データの保存に失敗しました");
return;
}
setRecords([...records, data[0]]);
} catch (e) {
setErrorMessage("予期しないエラーが発生しました: " + e.message);
} finally {
setLoading(false);
}
};
throw の使いどころ
バリデーションのように予測できる「入力ミス」などは if
+ return
で十分ですが、想定外の異常(たとえば data
が null
)などは throw
で catch に流すべきです。今回supabase内でエラーが起きた時を想定し、null
の設定をしました。
if (!data) {
throw new Error("データが取得できませんでした");
}
このように throw
したエラーは、catch
で受け取ってユーザーに表示することで、想定外の失敗にも丁寧に対応できます。
if...elseとの違い
if...else
は「条件に応じて分ける処理」
try...catch
は「エラーが出たときのための処理」
if は「〇〇ならこうする、違うならこうする」
try は「やってみる → ダメだったらこっちで対応する」
という使い分けをしています。
エラーが起きるかどうかは if では予測できない ので、そういうときに try
を使います。
参考リンク