エラー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回だけ必ず失敗します。