【Vue.js】ページネーション実装時にuseRouteでUncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘query’)とエラーが出た

Vue.jsでページネーションを実装していましたが、Vue Routerのインスタンスになぜかアクセスできず困ったため、備忘録として残します。

バージョン
  • vue 3.3.11
  • vue-router 4.2.5
  • vuetify 3.4.9
記事の信頼性
  • ぼくは独学で未経験から従業員300名以上の自社開発企業へ転職しました。
  • 実務ではVue.jsとRailsを毎日書いています。
  • 初心者や駆け出しエンジニアがつまづくポイントも身をもってよく理解しています。
目次

問題

Vuetifyを使って以下のようにページネーションの実装を試みていました。

<script setup lang="ts">
import { reactive, ref, watch } from 'vue'
import useSamplePage from './composables/useSamplePage'
import { useRoute, useRouter } from 'vue-router';

const { samplePage, setupSamplePage } = useSamplePage()

const route = useRoute()

watch(() => route.query.page, async () => {
  setupSamplePage()
}, {
  immediate: true
})

const currentPage = ref(1)

const router = useRouter()

const onChange = () => {
  if (!currentPage.value) return

  router.push({
    path: `/sample`,
    query: { page: currentPage.value },
  })
}
</script>

<template>
  <v-container>
    <p>{{ `sampleStateの値: ${sampleState}` }}</p>
    <v-pagination v-model="currentPage" :length="5" @click="onChange" />
  </v-container>
</template>
import { ref } from "vue";
import { useRoute } from "vue-router";

const useSamplePage = () => {
  const samplePage = ref(0);

  const setupSamplePage = () => {
    const route = useRoute();

    const page = route.query.page || 1;
    if (page) {
      samplePage.value = Number(page);
    }
  };

  return {
    samplePage,
    setupSamplePage,
  };
}

export default useSamplePage;

ページネーションをクリックするとroute.query.pageに選択されたページ番号が格納され、ルーティングが変わります。

そしてroute.query.pageの値をwatchで監視し、変更があるたびにComposablesとして切り出したsetupSamplePage関数が実行される仕様です。

これをブラウザで表示すると、以下のようになります↓

あとはページを選択するたびに「pageの値: 」も連動して切り替わってくれれば良いのですが、うまくいかず、、。

番号をクリックするとURLはhttp://localhost:5173/sample?page=2となっていますが、画面の番号は1のままです。

コンソールを確認するとエラーが出ていました、、。

どうやらuseSamplePage.tsファイルのconst page = route.query.page || 1;の箇所でrouteオブジェクトがundefinedになっているようです。

しかし、setupSamplePage関数の中でconst route = useRoute();と記述しているのに、なぜrouteがundefinedになってしまうかが分からず、時間をかけてしまいました、、。

解決方法

結論として、useSamplePage.tsの中にconst route = useRoute();を記述せず、SampleView.vueの中でrouteオブジェクトを取得し、setupSamplePage関数に引数として渡すことでエラーが解消できました。

修正後のファイルは以下のとおりです。

<script setup lang="ts">
import { reactive, ref, watch } from 'vue'
import useSamplePage from './composables/useSamplePage'
import { useRoute, useRouter } from 'vue-router';

const { samplePage, setupSamplePage } = useSamplePage()

const route = useRoute()

watch(() => route.query.page, async () => {
  // 引数として route.query.page を渡す
  setupSamplePage(Number(route.query.page))
}, {
  immediate: true
})

watch(() => route.query.page, async () => {
  // 引数として route.query.page を渡す
  const page = Number(route.query.page) || 1;
  setupSamplePage(page)
}, {
  immediate: true
})

const currentPage = ref(1)

const router = useRouter()

const onChange = () => {
  if (!currentPage.value) return

  router.push({
    path: `/sample`,
    query: { page: currentPage.value },
  })
}
</script>

<template>
  <v-container>
    <p>{{ `samplePageの値: ${samplePage}` }}</p>
    <v-pagination v-model="currentPage" :length="5" @click="onChange" />
  </v-container>
</template>
import { ref } from "vue";
import { useRoute } from "vue-router";

const useSamplePage = () => {
  const samplePage = ref(0);

  // route.query.page の値を関数内で取得せず、引数として受け取る
  const setupSamplePage = (page: number) => {
    samplePage.value = Number(page);
  };

  return {
    samplePage,
    setupSamplePage,
  };
}

export default useSamplePage;

setup関数の実行が完了する前の時点ではVue Routerがまだ準備中の状態なので、useRouteを実行してもrouteオブジェクトが取得されず、冒頭のエラーが発生していたようです。

これでもう一度ブラウザを確認すると、、

選択したページネーションに応じて、画面の表示も切り替わるようになりました!

おわりに

まだ根本から理解できていない気がしていますが、とりあえず解決方法だけまとめました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

未経験でSESから従業員300名以上の自社開発企業に転職しました。業務や個人開発で直面した問題や、転職・学習の経験を発信していきます。

コメント

コメントする

目次