非同期操作(API呼び出し)には複数の可能な結果があるため、単なるデータだけでなくモデル化する必要があります。ローディング、成功、エラー状態を表現する必要があります。これを適切にモデル化することで、古いデータを表示したり、フィードバックがないなどのUI バグを防ぐことができます。
単純なアプローチとその欠陥
js
[data, setData] = ();
[loading, setLoading] = ();
[error, setError] = ();
独立したフラグでは、矛盾した組み合わせ(ローディングかつエラーかつデータが同時に存在する)が可能になり、これは起こるべきではない状態です。これは混乱を招くUI バグの原因になります。
// ✅ a single `status` makes states mutually exclusive
const [state, setState] = useState({ status: "idle", data: null, error: null });
// status is one of: 'idle' | 'loading' | 'success' | 'error'
async function load() {
setState({ status: "loading", data: null, error: null });
try {
const data = await api.get();
setState({ status: "success", data, error: null });
} catch (e) {
setState({ status: "error", data: null, error: e.message });
}
}
単一の status フィールドは状態を相互に排他的にします。ローディングとエラー状態を同時に持つことはできません。レンダリングはきれいな switch になります:
switch (state.status) {
case "loading": return <Spinner />;
case "error": return <Error message={state.error} />;
case "success": return <List data={state.data} />;
default: return <Idle />;
}
idle — hasn't started yet (vs loading)
empty — succeeded but no results (show "No items" not a blank screen)
refetching— has old data AND is loading new (show data + a subtle spinner)
// React Query / SWR model all of this for you
const { data, isLoading, isError, error } = useQuery({ queryKey: ["x"], queryFn });
サーバー状態ライブラリは、これらの状態(キャッシング、refetch、stale-while-revalidate を含む)を組み込みで提供します。通常は手動で実装するよりも優れています。
非同期状態を適切にモデル化する(散在したブール値ではなく単一のステータスとして)ことで、UI バグの全体的なクラスを排除します(矛盾した状態、ローディング/エラー/空のフィードバックが欠落している)。レンダリングロジックがきれいで網羅的になります。
すべての状態(idle、loading、success、error、empty、refetching)を認識することで、より良い UX につながります。また、サーバー状態ライブラリがすべてを処理することを理解することで、データフェッチングコードがシンプルで堅牢になります。