useFetch

useFetch

リアクティブな Fetch API は、リクエストの中止、リクエストが発火する前のインターセプト、URLが変更されたときの自動再フェッチ、事前定義されたオプションで独自の useFetch を作成する機能を提供します。

Vue Schoolのこの無料ビデオレッスンでuseFetchを学びましょう!

::: tip Nuxt 3と一緒に使用する場合、この関数はNuxtの組み込みの useFetch() を優先して自動インポートされません。VueUseからの関数を使用したい場合は、明示的にインポートしてください。 :::

使用法

基本的な使用法

useFetch 関数は、単にURLを提供することで使用できます。URLは文字列または ref のいずれかです。data オブジェクトにはリクエストの結果が含まれ、error オブジェクトにはエラーが含まれ、isFetching オブジェクトはリクエストがロード中かどうかを示します。

import { useFetch } from '@vueuse/core'

const { isFetching, error, data } = useFetch(url)

非同期使用法

useFetch は通常のフェッチと同様に待機することもできます。コンポーネントが非同期である場合、それを使用するコンポーネントは <Suspense> タグでラップする必要があります。サスペンスAPIについての詳細は 公式Vue 3ドキュメント を参照してください。

import { useFetch } from '@vueuse/core'
// ---cut---
const { isFetching, error, data } = await useFetch(url)

URL変更時の再フェッチ

URLパラメータに ref を使用すると、URLが変更されたときに useFetch 関数が自動的に別のリクエストをトリガーします。

import { useFetch } from '@vueuse/core'
// ---cut---
const url = ref('https://my-api.com/user/1')

const { data } = useFetch(url, { refetch: true })

url.value = 'https://my-api.com/user/2' // 別のリクエストをトリガーします

リクエストの即時発火を防ぐ

immediate オプションをfalseに設定すると、execute 関数が呼び出されるまでリクエストの発火を防ぎます。

import { useFetch } from '@vueuse/core'
// ---cut---
const { execute } = useFetch(url, { immediate: false })

execute()

リクエストの中止

useFetch 関数から abort 関数を使用してリクエストを中止できます。canAbort プロパティはリクエストが中止可能かどうかを示します。

import { useFetch } from '@vueuse/core'
// ---cut---
const { abort, canAbort } = useFetch(url)

setTimeout(() => {
  if (canAbort.value)
    abort()
}, 100)

timeout プロパティを使用してリクエストを自動的に中止することもできます。指定されたタイムアウトに達すると abort 関数が呼び出されます。

import { useFetch } from '@vueuse/core'
// ---cut---
const { data } = useFetch(url, { timeout: 100 })

リクエストのインターセプト

beforeFetch オプションは、リクエストが送信される前にリクエストオプションとURLを変更するためにインターセプトできます。

import { useFetch } from '@vueuse/core'
// ---cut---
const { data } = useFetch(url, {
  async beforeFetch({ url, options, cancel }) {
    const myToken = await getMyToken()

    if (!myToken)
      cancel()

    options.headers = {
      ...options.headers,
      Authorization: `Bearer ${myToken}`,
    }

    return {
      options,
    }
  },
})

afterFetch オプションは、レスポンスデータが更新される前にインターセプトできます。

import { useFetch } from '@vueuse/core'
// ---cut---
const { data } = useFetch(url, {
  afterFetch(ctx) {
    if (ctx.data.title === 'HxH')
      ctx.data.title = 'Hunter x Hunter' // レスポンスデータを変更します

    return ctx
  },
})

onFetchError オプションは、updateDataOnErrortrue に設定されている場合、レスポンスデータとエラーが更新される前にインターセプトできます。

import { useFetch } from '@vueuse/core'
// ---cut---
const { data } = useFetch(url, {
  updateDataOnError: true,
  onFetchError(ctx) {
    // ctx.data は 5xx レスポンス時に null になることがあります
    if (ctx.data === null)
      ctx.data = { title: 'Hunter x Hunter' } // レスポンスデータを変更します

    ctx.error = new Error('Custom Error') // エラーを変更します
    return ctx
  },
})

console.log(data.value) // { title: 'Hunter x Hunter' }

リクエストメソッドと戻り値の設定

リクエストメソッドと戻り値の型は、useFetch の末尾に適切なメソッドを追加することで設定できます。

import { useFetch } from '@vueuse/core'
// ---cut---
// リクエストはGETメソッドで送信され、データはJSONとして解析されます
const { data } = useFetch(url).get().json()

// リクエストはPOSTメソッドで送信され、データはテキストとして解析されます
const { data } = useFetch(url).post().text()

// またはオプションを使用してメソッドを設定します

// リクエストはGETメソッドで送信され、データはblobとして解析されます
const { data } = useFetch(url, { method: 'GET' }, { refetch: true }).blob()

カスタムインスタンスの作成

createFetch 関数は、提供された事前設定オプションを持つ useFetch 関数を返します。これは、同じベースURLや認証ヘッダーが必要なアプリケーション全体でAPIとやり取りするのに便利です。

import { createFetch } from '@vueuse/core'
// ---cut---
const useMyFetch = createFetch({
  baseUrl: 'https://my-api.com',
  options: {
    async beforeFetch({ options }) {
      const myToken = await getMyToken()
      options.headers.Authorization = `Bearer ${myToken}`

      return { options }
    },
  },
  fetchOptions: {
    mode: 'cors',
  },
})

const { isFetching, error, data } = useMyFetch('users')

事前設定されたインスタンスと新しく生成されたインスタンスの間で beforeFetchafterFetchonFetchError の動作を制御したい場合。combination オプションを提供して overwrite または chaining を切り替えることができます。

import { createFetch } from '@vueuse/core'
// ---cut---
const useMyFetch = createFetch({
  baseUrl: 'https://my-api.com',
  combination: 'overwrite',
  options: {
    // 事前設定されたインスタンスの beforeFetch は、新しく生成されたインスタンスが beforeFetch を渡さない場合にのみ実行されます
    async beforeFetch({ options }) {
      const myToken = await getMyToken()
      options.headers.Authorization = `Bearer ${myToken}`

      return { options }
    },
  },
})

// useMyFetch の beforeFetch を使用
const { isFetching, error, data } = useMyFetch('users')

// カスタム beforeFetch を使用
const { isFetching, error, data } = useMyFetch('users', {
  async beforeFetch({ url, options, cancel }) {
    const myToken = await getMyToken()

    if (!myToken)
      cancel()

    options.headers = {
      ...options.headers,
      Authorization: `Bearer ${myToken}`,
    }

    return {
      options,
    }
  },
})

afterFetch または onFetchErrorexecute メソッドを呼び出すことでリクエストを再実行できます。ここではトークンをリフレッシュする簡単な例を示します:

import { createFetch } from '@vueuse/core'
// ---cut---
let isRefreshing = false
const refreshSubscribers: Array<() => void> = []

const useMyFetch = createFetch({
  baseUrl: 'https://my-api.com',
  options: {
    async beforeFetch({ options }) {
      const myToken = await getMyToken()
      options.headers.Authorization = `Bearer ${myToken}`

      return { options }
    },
    afterFetch({ data, response, context, execute }) {
      if (needRefreshToken) {
        if (!isRefreshing) {
          isRefreshing = true
          refreshToken().then((newToken) => {
            if (newToken.value) {
              isRefreshing = false
              setMyToken(newToken.value)
              onRefreshed()
            }
            else {
              refreshSubscribers.length = 0
              // リフレッシュトークンエラーを処理
            }
          })
        }

        return new Promise((resolve) => {
          addRefreshSubscriber(() => {
            execute().then((response) => {
              resolve({ data, response })
            })
          })
        })
      }

      return { data, response }
    },
    // または updateDataOnError を使用して onFetchError を使用
    updateDataOnError: true,
    onFetchError({ error, data, response, context, execute }) {
      // afterFetch と同様
      return { error, data }
    },
  },
  fetchOptions: {
    mode: 'cors',
  },
})

async function refreshToken() {
  const { data, execute } = useFetch<string>('refresh-token', {
    immediate: false,
  })

  await execute()
  return data
}

function onRefreshed() {
  refreshSubscribers.forEach(callback => callback())
  refreshSubscribers.length = 0
}

function addRefreshSubscriber(callback: () => void) {
  refreshSubscribers.push(callback)
}

const { isFetching, error, data } = useMyFetch('users')

イベント

onFetchResponseonFetchError は、それぞれフェッチリクエストのレスポンスとエラー時に発火します。

import { useFetch } from '@vueuse/core'
// ---cut---
const { onFetchResponse, onFetchError } = useFetch(url)

onFetchResponse((response) => {
  console.log(response.status)
})

onFetchError((error) => {
  console.error(error.message)
})