Reactではfetchではなく、axios + Tanstack Queryが推奨されている理由

React公式では「axios + TanStack Query」推奨

React開発ではfetch単体よりも、フレームワークを使用していない場合は、axios + TanStack Queryのようにオープンソースのソリューションを使用することが推奨されています。

エフェクトでのデータ取得に代わる良い方法は? - React

axiosはfetchの利便性の悪さから使用されているケースが多いですが、TanStack Queryが使用されているケースは少ないです。

axios + TanStack Queryを使用したほうが状態管理コード(useState)がなくなるので、コードの可読性が良くなります。

TSX
// fetchのコード
setLoading(true)
try {
  const res = await fetch(url)
  if (!res.ok) throw new Error('error')
  const data = await res.json()
  setData(data)
} catch (e) {
  setError(e)
} finally {
  setLoading(false)
}
TSX
// axios + TanStack Query のコード
const fetchData = async (url: string) => {
  const res = await axios.get(url)
  return res.data
}

const {
  data,
  isLoading,
  isError,
  error
} = useQuery({
  queryKey: ['data', url],
  queryFn: () => fetchData(url)
})

axios + TanStack Queryを使用したサンプル

まず、baseURL付きのaxiosインスタンスを作成します。

このサンプルではdummyjsonを使用しているため、baseURLが「https://dummyjson.com」になっていますが、一般的なWebサイトであれば「/api」とかになります。

api.ts
import axios from 'axios'

export const api = axios.create({
  baseURL: 'https://dummyjson.com',
  timeout: 10000,
})

axiosではtimeoutのデフォルト値が0のため、指定しないとどれだけ待ってもタイムアウトしません。

なので、必ずtimeoutの時間をミリ秒で指定してください。

次にTanStack Queryを使用するためのProvider(土台)を用意します。

AppProvider.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'

const queryClient = new QueryClient()

export function AppProvider({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  )
}

Providerを用意したらmain.tsxで読み込んで、<AppProvider><App /></AppProvider>のように囲みます。

main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import { AppProvider } from './providers/AppProvider'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <AppProvider>
      <App />
    </AppProvider>
  </StrictMode>
)

最後にApp.tsxでダミーのJSONのAPIを読み込んで表示するコードを以下のように書けば完成です。

Tanstack QueryのuseQueryを使用することで、data, isLoading, isError, errorのそれぞれがuseQueryで取得できるので、状態管理コード(useState)やtry ... catch文を使わない可読性の良いコードになっています。

main.tsx
import { useQuery } from '@tanstack/react-query'
import { api } from './lib/api'

const fetchProducts = async () => {
  const res = await api.get('/products?select=title,price&delay=1000')
  return res.data
}

function App() {
  const { data, isLoading, isError, error } = useQuery({
    queryKey: ['products'], // クエリを識別するためのキー 
    queryFn: fetchProducts, // データを取得する非同期関数
    staleTime: 10000,       // キャッシュが使われる時間
  })

  if (isLoading) {
    return <p>Loading...</p>
  }

  if (isError) {
    return <p>Error: {(error as Error).message}</p>
  }

  return (
    <>
      <h1>Products</h1>
      <ul>
        {data.products.map((product: any) => (
          <li key={product.id}>
            {product.title} - ${product.price}
          </li>
        ))}
      </ul>
    </>
  )
}

export default App

axios + TanStack Query GET sample

Reactの基礎がわかる方であれば、axios + Tanstack Queryが推奨されている理由をこのコードを見ただけで理解することができ、これを使わずにはいられないでしょう。