エラーUI

fetch は 500 では失敗しない

エラーの画面を作る前に、そもそも失敗をどう捕まえるかを確かめます。

const res = await fetch("/api/items"); const data: Item[] = await res.json();

この書き方だと、サーバーが 500 を返しても catch には来ません。fetch は「相手に届いて返事が返ってきた」時点で成功とみなすからです。catch に落ちるのは、回線が切れているなど返事そのものが無かったときだけです。

500 のときも中身は返ってきます。中身はエラーの説明なので、それを商品の配列として map に掛けると、そこで画面ごと落ちます。実際、この回の初期コードは最初の取得で真っ赤になります。

返事の中身が正常かどうかは、自分で見ます。

if (!res.ok) throw new Error("読み込みに失敗しました");

res.ok は 200 番台なら true です。ここで throw すれば、呼んだ側の catch でまとめて受けられます。

エラー画面に要るもの

失敗したことを伝えるだけの画面は、使う人を行き止まりに置き去りにします。エラーの画面には次の3つを置きます。

  • 何が起きたのか。専門用語ではなく普通の言葉で
  • 使う人の操作が悪かったわけではないこと
  • 次にできること。多くの場合はもう一度試すこと

「Error 500」とだけ出す画面は、この3つのどれも満たしていません。

もう一度取りに行く

useEffect は依存配列の中身が変わったときに動きます。つまり、押すたびに変わる値を1つ足せば、再試行のボタンが作れます。

const [reloadKey, setReloadKey] = useState(0); useEffect(() => { // 取得の処理 }, [category, reloadKey]); <button className="retry" onClick={() => setReloadKey(reloadKey + 1)}> 再試行 </button>

取り直す前に setState({ status: "loading" }) へ戻すのを忘れないでください。戻さないと、押しても画面がエラーのまま固まって見えます。

演習

エラーの画面に再試行のボタンを足します。この回の API は最初の1回だけ必ず失敗します。

ヒント

App.tsx
mock-api.json
App.tsx
mock-api.json
プレビュー

できているか

  • 失敗したらエラー文が出る
  • 再試行のボタンがある
  • 再試行のボタンがエラー表示の中にある
  • 再試行を押すと一覧が出る