はじめに
React + TypeScript でアプリを作っているときに、ESLint の no-unsafe-finally エラーが出ました。
問題
非同期処理( try ... finally )の中に finally ブロックを使ってローディング状態をリセットするコードを書いていたところ、以下のような ESLint エラーが出ました。
Unsafe usage of ReturnStatement(no-unsafe-finally)
該当コードはこんな感じ
useEffect(() => {
let cancelled = false;
const load = async () => {
setLoading(true);
try {
const response = await fetchDashboard();
if (cancelled) return;
setData(response);
} catch (err) {
if (cancelled) return;
setError((err as Error).message ?? "ダッシュボードの読み込みに失敗しました");
} finally {
if (cancelled) return; // ← ここでエラー
setLoading(false);
}
};
load();
return () => {
cancelled = true;
};
}, [user]);
解決方法
どうやら、try ... finally ブロックの中では return / throw / break / continue を使ってはいけないようでした。
finally の中で return をすると、try-catch の結果を上書きしてしまい、意図しない制御フローになるそうです。
代わりに、単純な if で制御しました。
finally {
if (!cancelled) {
setLoading(false);
}
}
なぜこんな書き方をしたのか
正直、「try と catch の中で if (cancelled) return を書いているから、finally にも同じように書いとけばいいや」という安直な考えでした(*´-`)
おわりに
finally は「どんな場合でも最後に実行される」という性質があるので、return を書いてしまうと予期しない挙動になりやすいです。
今回のような useEffect 内のクリーンアップ処理では、return せずにガードで分岐させるのが良さそうです。
参考