🗂
tRPCで知っておくべき3つのデータ操作パターン:useQuery、useMutation、utils.fetchの使い分け完全ガイド
tRPCで混乱しがちな3つのデータ操作パターンを完全解説
tRPCを使い始めたとき、「データ取得や更新の方法が複数あるけど、どれを使えばいいの?」と迷った経験はありませんか?
実際、tRPCには主に3つのパターンがあります:
-
useQuery
- 自動的なデータ取得 -
useMutation
- データ変更操作 -
utils.fetch
- 手動でのAPI呼び出し
この記事では、それぞれの特徴と適切な使い分け方を、実際のコード例とともに詳しく解説します。
まず理解しておきたい3つのパターン
パターン | 用途 | 実行タイミング | 状態管理 |
---|---|---|---|
useQuery |
データ表示 | 自動(マウント時) | 自動 |
useMutation |
データ変更 | 手動(イベント時) | 自動 |
utils.fetch |
一回限り処理 | 手動(イベント時) | 手動 |
useQuery:画面表示用データの自動取得
特徴
- コンポーネントマウント時に自動実行
- キャッシュ機能で高速化
- ローディング・エラー状態を自動管理
実用例:ユーザー一覧の表示
const UserListPage = () => {
const {
data: users,
isLoading,
error
} = trpc.user.getAll.useQuery()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
{users?.map(user => (
<UserCard key={user.id} user={user} />
))}
</div>
)
}
いつ使う?
- ページ読み込み時に必要なデータ
- リアルタイムで更新が必要なデータ
- 複数の場所で使い回すデータ
useMutation:データ変更操作の状態管理
特徴
- ボタンクリックなどのイベントで実行
- 成功・失敗のコールバック処理
- ローディング状態の自動管理
実用例:ユーザー作成フォーム
const CreateUserForm = () => {
const utils = trpc.useUtils()
const createUserMutation = trpc.user.create.useMutation({
onSuccess: () => {
toast.success('ユーザーが作成されました')
// キャッシュを更新して一覧に反映
utils.user.getAll.invalidate()
},
onError: (error) => {
toast.error(`エラー: ${error.message}`)
}
})
const handleSubmit = (formData) => {
createUserMutation.mutate(formData)
}
return (
<form onSubmit={handleSubmit}>
{/* フォーム要素 */}
<button
type="submit"
disabled={createUserMutation.isLoading}
>
{createUserMutation.isLoading ? '作成中...' : '作成'}
</button>
</form>
)
}
いつ使う?
- フォーム送信
- データの作成・更新・削除
- 成功時にUIを更新したい操作
utils.fetch:一回限りの手動API呼び出し
特徴
- 必要な時だけ実行
- Promise形式で結果を取得
- 状態管理は自分で行う
実用例:ファイルダウンロード
const FileDownloadButton = ({ fileId }) => {
const [downloading, setDownloading] = useState(false)
const utils = trpc.useUtils()
const handleDownload = async () => {
try {
setDownloading(true)
const data = await utils.file.getDownloadUrl.fetch({
fileId
})
// ダウンロード実行
const link = document.createElement('a')
link.href = data.url
link.download = data.fileName
link.click()
toast.success('ダウンロードを開始しました')
} catch (error) {
toast.error('ダウンロードに失敗しました')
} finally {
setDownloading(false)
}
}
return (
<button
onClick={handleDownload}
disabled={downloading}
>
{downloading ? 'ダウンロード中...' : 'ダウンロード'}
</button>
)
}
いつ使う?
- ダウンロード処理
- 条件付きのデータ取得
- 複雑なエラーハンドリングが必要な場合
実践的な使い分けフローチャート
よくある間違いパターンと修正例
❌ 間違い:useQueryでデータ変更
// これはダメ
const { data } = trpc.user.delete.useQuery({ id: userId })
✅ 正解:useMutationでデータ変更
const deleteUserMutation = trpc.user.delete.useMutation({
onSuccess: () => utils.user.getAll.invalidate()
})
まとめ:適切なパターンの選び方
-
画面に表示するデータ →
useQuery
- 自動取得、キャッシュ、リアルタイム更新
-
ユーザーアクションでデータ変更 →
useMutation
- フォーム送信、CRUD操作、状態管理
-
特定条件での一回限り処理 →
utils.fetch
- ダウンロード、条件付き取得、カスタムエラーハンドリング
この使い分けを意識することで、tRPCの恩恵を最大限活用した、保守性の高いReactアプリケーションを構築できます。
Discussion