目的
データ取得フックで data の表示だけ先に実装してしまい、isLoading / error の対応が抜けることを防ぎます。
RTK Query、SWR、TanStack Query など、一般的なデータフェッチライブラリで共通するパターンを想定しています。
方針
- データフェッチ用フックの命名を
useGet*に統一します(生成フックでもラッパーでも可) -
useGet*を呼び出す場合、errorとisLoadingを分割代入で取り出すことを ESLint で強制します
実装例
const { data, error, isLoading } = useGetUser(id);
if (isLoading) return <span>読み込み中...</span>;
if (error) return <span>エラーが発生しました</span>;
return <div>{data?.name}</div>;
ESLint で強制します
行うことは次の 2 点です。
-
useGet*()の戻り値を丸ごと代入しない(分割代入を強制) - 分割代入する場合、
errorとisLoadingを必須にする
.eslintrc.* 例
// .eslintrc.cjs
module.exports = {
rules: {
"no-restricted-syntax": [
"error",
// (1) useGet* の戻り値を丸ごと代入禁止
{
selector:
"VariableDeclarator[id.type='Identifier'][init.type='CallExpression'][init.callee.name=/^useGet/]",
message:
"useGet* の戻り値は分割代入して error と isLoading を取り出してください",
},
// (2) error が無い分割代入を禁止
{
selector:
"VariableDeclarator[id.type='ObjectPattern'][init.type='CallExpression'][init.callee.name=/^useGet/]" +
":not(:has(Property[key.name='error']))",
message: "useGet*: error を必ず取り出してください",
},
// (3) isLoading が無い分割代入を禁止
{
selector:
"VariableDeclarator[id.type='ObjectPattern'][init.type='CallExpression'][init.callee.name=/^useGet/]" +
":not(:has(Property[key.name='isLoading']))",
message: "useGet*: isLoading を必ず取り出してください",
},
],
},
};
const { error: fetchError } = ...のように別名にしても、元キーがerrorであれば問題ありません。また、TanStack Query v5 などで
isLoadingではなくisPendingを使用する場合は、(3) のkey.nameを適宜書き換えてください。
補足: error を使用しないとどうなる?
_error のように未使用回避で捨てる運用もできますが、できれば if (error) 分岐で error を使用することを推奨します。
- 親コンポーネントでエラーハンドリングしていても、リファクタリングや再利用で前提が崩れる可能性がある
- フックを呼び出すコンポーネント単位で完結したエラーハンドリングをしておけば、どこで使われても安全
まとめ
-
useGet*をデータフェッチ専用の命名にします -
useGet*ではerrorとisLoadingを必ず取り出すことを ESLint で強制します