結論
僕は以下のように考えています。
- useEffect内ではfetchは行わない
- アプリケーション全体でTanStack QueryかSWRを使うのが丸い
そう思う理由
結論に辿りついた道筋を説明します。
まず、React公式 useEffectの説明で、以下のように書いてあります。
エフェクトを使って、コンポーネントに必要なデータをフェッチ(fetch, 取得)することができます。ただしフレームワークを使用している場合は、エフェクトを自力で記述するよりも、フレームワークのデータフェッチメカニズムを使用する方がはるかに効率的であることに注意してください。
NextJSでいうと、「Server Componentsでデータフェッチしてください。もちろんfetchAPIは使用しても全然良いです。」という意味だと受け取っていて、そうするべきだと思っています。
一方で、token認証を使用していたり、何らかの理由があったりとかなかったりとかでClient Componentsでデータフェッチをする必要があるかもしれません。
ここで、Client Componentsはasync関数にできないのです。
つまり、Server Componentで行う以下のようなデータフェッチはできません(async await使えないから)。
'use client'
// この関数はエラーとなる
export default async function Sample() {
const data = await fetch('url')
// 以下略
}
then構文使えばできるとは思うけど、アプリ全体で書き方は統一したいしなぁと(これは甘えなのか?)。
ここまで来て、確かにfetchAPIを使ってuseEffectをasync関数にして、その中でデータフェッチをしようという発想になります。
ただ、React公式の説明は以下のようなことも言っています。
フレームワークを使用している場合、組み込みのデータフェッチ機構を使用してください。モダンな React フレームワークには、効率的で上記の欠点がないデータフェッチ機構が統合されています。
それ以外の場合は、クライアントサイドキャッシュの使用や構築を検討してください。一般的なオープンソースのソリューションには、React Query、useSWR、および React Router 6.4+ が含まれます。自分でソリューションを構築することもできます。その場合、エフェクトを内部で使用しつつ、リクエストの重複排除、レスポンスのキャッシュ、ネットワークのウォーターフォールを回避するためのロジック(データのプリロードやルーティング部へのデータ要求の巻き上げ)を追加することになります。
今回はこの説明における「それ以外は」の部分に当てはまると考えています。
とすると、useEffectでデータフェッチをするのではなくTanStack QueryかSWRを使うのが良い気がする。
なので、以下のような方針が定まってきました。
- Server ComponentsではfetchAPIでデータフェッチをする
- Client ComponentsではTanStack QueryかSWRでデータフェッチをする
これでもまぁ良いには良いけども、という感じですが、流石にデータフェッチのライブラリは統一したいなぁという気持ちです(管理とか面倒だし)。
その気持ちより、以下の結論に辿りつきました。
コンポーネントの種類に関係なく、データフェッチはTanStackQueryとかSWRを使う
最後に
今回の結論の方針を採用すると、余程のことがない限りuseEffect内でデータフェッチをすることはないし、多分公式の方針に沿っているのかなとは思っています。
結構な人が当たり前のようにこの方針を採用していると想像はしているけども、この結論にたどり着くまでを言語化する作業は楽しかったです。
あくまで私見です。
誤りがあれば避難ではなく指摘、提案してください。