1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ローディングでのエラー処理をtry-catch-finallyでまとめた【非同期処理】

Posted at

ローディング状態の制御などの非同期処理では処理中であることを示す、try...catchを使います。
さらにfinallyを併用することで、ローディングのような「必ず実行したい処理」を確実に実行することができたので、今回備忘録としてまとめます。

前提条件

  • Reactを使ったコンポーネント内で状態管理をしている
  • SupabaseなどのAPIから非同期でデータを取得・送信するケースを扱っている
  • ローディング状態(loading)はuseStateで管理されている
  • 関数にはasyncが使われており、非同期処理の完了をawaitで待っている

try...catch...finallyとは

try...catch

非同期処理やエラーが起こりうる処理をtryブロックに書きます。そこでエラーが発生すると、処理は中断され、catchブロックに移ります。

try {
  // エラーが起こるかもしれない処理
} catch (error) {
  // エラー時の処理
}

finally

finallyブロックは、trycatchのあとに必ず実行される部分です。たとえreturnthrowがあっても実行されます。処理の後片付けや状態のリセットに適しています。

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 で十分ですが、想定外の異常(たとえば datanull)などは throw で catch に流すべきです。今回supabase内でエラーが起きた時を想定し、nullの設定をしました。

if (!data) {
  throw new Error("データが取得できませんでした");
}

このように throw したエラーは、catch で受け取ってユーザーに表示することで、想定外の失敗にも丁寧に対応できます。

if...elseとの違い

if...else は「条件に応じて分ける処理」
try...catch は「エラーが出たときのための処理」

if は「〇〇ならこうする、違うならこうする」
try は「やってみる → ダメだったらこっちで対応する」
という使い分けをしています。
エラーが起きるかどうかは if では予測できない ので、そういうときに tryを使います。

参考リンク

1
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?