3状態を型で表す
通信には3つの結末がある
第2章で、商品一覧を API から取れるようになりました。動くには動きますが、いまの画面には足りないものがあります。
const [items, setItems] = useState<Item[]>([]);この形だと、読み込んでいる途中も、通信に失敗したときも、本当に0件のときも、使う人には全部「空っぽの一覧」に見えます。3つの違う出来事が、1つの見た目につぶれています。
旗を足すと組み合わせが増える
よくある直し方は、旗になる state を足すことです。
const [items, setItems] = useState<Item[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");これでも一応は書けます。ただ、この3つは別々に動くので「読み込み中なのにエラー文も出ている」「成功しているのに前のエラーが残っている」といった、ありえない組み合わせまで表せてしまいます。旗が増えるほど、書き忘れた組み合わせが画面に出ます。
1つのユニオン型にまとめる
取りうる姿が3つなら、その3つをそのまま型に書きます。
type LoadState =
| { status: "loading" }
| { status: "success"; items: Item[] }
| { status: "error"; message: string };TypeScript入門で出てきた判別可能なユニオン型です。共通の目印 (ここでは status) で枝を見分けます。3つのうちどれか1つにしかなれないので、ありえない組み合わせはそもそも書けません。
枝の中でしか中身を読めない
{state.status === "loading" && <p className="loading">読み込み中</p>}
{state.status === "error" && <p className="error">{state.message}</p>}
{state.status === "success" && <div className="list">{/* 一覧 */}</div>}state.items が読めるのは success の枝の中だけです。読み込み中に items を触るコードは、書いた時点で赤くなります。状態の抜け漏れを、目で見て気を付けるのではなく型で潰すのがこの回の要点です。
演習
一覧の取得を LoadState で書き直し、読み込み中と成功を出し分けます。