GAミント至上主義

Web Monomaniacal Developer.

NuxtJSでaxiosのリクエストをキャンセルする

NuxtJSでSSR + SPAでコンテンツをaxiosで取得するよくあるページを作っていたら、リンクを連打するとこんな感じになった。
言葉で説明するの大変なのでブログ用にTwitterに動画あげた。


バージョンはここらへん。TypeScript、Composition APIを使ってます。

  "dependencies": {
    "@nuxtjs/axios": "^5.13.4",
    "@nuxtjs/composition-api": "^0.23.4",
    "@nuxtjs/proxy": "^2.1.0",
    "cookie-universal-nuxt": "^2.1.4",
    "core-js": "^3.12.1",
    "dayjs": "^1.10.4",
    "nuxt": "^2.15.6",
    "vue-slick-carousel": "^1.0.6",
    "vuejs-paginate": "^2.1.0"
  },

解決策としては、フォームみたいに、完了するまでクリックできなくするっていう方法もあるけど、ページ送りみたいな普通のリンクではやりたくない。

そこで、連打されたとき前回のをキャンセルする方法ないかと調べたところ、axiosにそういう機能がついてることを知った。

NuxtJSのaxiosモジュールを使っているけど使い方はaxiosと同じ
https://axios.nuxtjs.org/usage/#cancel-token


最初Promiseでできるかと思ったけど難しそうだった。


やってることはこの記事と同じだけど、NuxtJSだったり、Composition APIだったり、TypeScriptだったりするのでメモ。
siosio.hatenablog.com

開発中だけど必要そうなコードを抜粋、編集したやつ(動かない

import {
  defineComponent,
  useAsync,
  useContext,
  ssrRef,
  watch,
  ref
} from '@nuxtjs/composition-api'
import { JobListApiResponse, Job } from '../types'

export default defineComponent({
  validate({ query }) {
    if (!query.page) {
      return true
    } else if (typeof query.page === 'string') {
      return /^\d+$/.test(query.page)
    }
    return false
  },
  setup() {
    const { $axios, query } = useContext()
    const cancelToken = $axios.CancelToken
    const cancelSource = ref(cancelToken.source())
    const toPageNumber = (number: any): number => {
      // validateでチェックしてるので最低限
      if (typeof number === 'string') {
        return Number(number)
      }
      return 1
    }
    currentPage.value = toPageNumber(query.value.page)
    const fetchJobs = async () => {
      const page = toPageNumber(query.value.page)
      // 前回のをキャンセル
      cancelSource.value.cancel('連打かな?')
      // 新しくセット
      cancelSource.value = cancelToken.source()
      try {
        const response = await $axios.$get<JobListApiResponse>(
          '/api/path',
          {
            params: { page },
            cancelToken: cancelSource.value.token
          }
        )
        // response使ってデータをセットする処理
      } catch (error) {
        console.error(error)
      }
    }
    useAsync(fetchJobs)
    // queryに変更あったら再実行
    watch(query, fetchJobs)
    return {
      // 省略
    }
  }

cancelSourceはあとでファイルを分けたときようにrefを使ってます。

完了したやつをcancelしても特にエラーにはならないのでそのままキャンセルしてます。

あとリクエスト中のロード画面とかは必要そう。

こうすることで、最後のリクエストだけが残って思ってた挙動になりました