Next.js 14/15で「Server Actions脳」のままReact Queryを捨てると、現場は死ぬ
「全部Server Actionsで完結させるのがモダン」。最近、そんな甘い言葉を鵜呑みにして、React Queryをプロジェクトから剥がそうとする若手が増えている。正直に言おう。その設計、半年後の自分が地獄を見るぞ。
Server Actionsは強力だ。だが、それは「APIエンドポイントを作らなくて済む」という利便性と引き換えに、クライアントサイドの複雑な状態管理を放棄するリスクを孕んでいる。なぜ俺たちが苦労してReact Queryを使ってきたのか、その「理由」を忘れてはいけない。
1. 「Server Actions=万能」という幻想の正体
Server Actionsは、あくまで「サーバーへのデータ変更をトリガーする関数」だ。
一方で、React Queryは「サーバーのデータをクライアント側のキャッシュとして管理するエンジン」である。
この二つは役割が違う。全部Server Actionsに寄せると何が起きるか?
「サーバーのキャッシュ」と「ブラウザのRouter Cache」が別々に動き出し、画面が更新されない、あるいは古いデータが表示されるという「幽霊現象」が多発する。
#### なぜ「幽霊」が出るのか?
Next.jsの `revalidatePath` を呼ぶと、サーバー側のData Cacheはパージされる。しかし、クライアントサイドのRouter Cacheは即座には消えない。この「キャッシュの不整合」をReact Queryなしで解決しようとすると、結局 `router.refresh()` を連打するような、パフォーマンスを犠牲にした泥臭い実装に逃げることになる。
2. モダンな解:TanStack Query v5 × Server Actions
最新のTanStack Query v5では、Server Actionsを直接 `queryFn` に渡すのが正解だ。これなら、ネットワーク境界を意識しつつ、React Queryの強力なキャッシュ管理を享受できる。
// hooks/usePosts.ts
'use client'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { getPosts, createPost } from '@/app/actions'
export function usePosts() {
return useQuery({
queryKey: ['posts'],
queryFn: () => getPosts(), // Server Actionを直接叩く
})
}
export function useCreatePost() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (formData: FormData) => createPost(formData),
onSuccess: () => {
// キャッシュを明示的に無効化し、整合性を担保
queryClient.invalidateQueries({ queryKey: ['posts'] })
}
})
}
この構成の最大のメリットは、「楽観的アップデート(Optimistic Updates)」が圧倒的に楽になることだ。サーバーからのレスポンスを待たずにUIを更新し、失敗したら自動でロールバックする。この「手触り感」をServer Actions単体で実装しようとすれば、状態管理のスパゲッティコードが完成するのは目に見えている。
3. 「ネットワーク境界」という呪縛を理解する
Server Actionsの引数にDateオブジェクトやクラスインスタンスを放り込んでエラーになった経験はないか?
あれは、クライアントからサーバーへデータを送る際、JSONとしてシリアライズしなければならないという「物理的な壁」があるからだ。
React Queryを使えば、クライアント側でデータを整形し、シリアライズ可能な形に落とし込んでからServer Actionsに渡すという「バッファ」を作れる。この「境界線の制御」こそが、堅牢なアプリを作るエンジニアの腕の見せ所だ。
4. いつReact Queryを導入すべきか?(導入の閾値)
「全部Server Actionsでいいだろ」という判断が許されるのは、せいぜい「個人開発のLP」や「管理画面の極一部」までだ。以下の要素が1つでも含まれるなら、迷わずReact Queryを導入しろ。
| 複雑性の指標 | React Queryなしの末路 |
|---|---|
| ポーリング(自動更新) | `useEffect` と `setInterval` の地獄が待っている |
| 楽観的UI | 送信中のラグが気になり、UXが低下する |
| 複雑なキャッシュ依存 | Aを更新したらBとCも更新する、というロジックで死ぬ |
| オフライン対応 | 実装コストが跳ね上がり、メンテナンス不能になる |
結論:技術は「バランス」だ
Server Actionsは素晴らしい。だが、それは「React Queryを不要にする技術」ではない。「サーバーとクライアントの橋渡しを簡略化する技術」だ。
- 初期表示: Server Componentsで爆速化する。
- データ取得: React Queryでキャッシュを管理し、UXを磨く。
- データ更新: Server Actionsで安全にサーバーを叩く。
React Queryを捨てるのは、最強の武器を捨てて素手で戦場に行くのと同じだ。賢いエンジニアなら、道具は適材適所で使いこなせ。