Vue Routerを使用時に、queryを変更しても反映されなかった問題の解決 ~自作サービスで大変だったところその1~
こんにちは。フィヨルドブートキャンプ卒業生のふわ(@fuwa-syugyo)です。
先日リリースさせていただいた自作サービスで、大変だったところを振り返るシリーズを始めようと思います。記念すべき1記事目です!
環境
- TypeScript 4.9.5
- Vue 3.2.47 (Composition API)
- Vue-router 4.1.6
- Vite 4.1.2
概要
自作サービス(CreditsSpot)にて、検索結果画面からページを切り替えずに連続して検索するとページが更新されないという不具合が発生していました。
例えば「ミックスナッツ」と検索した後に、続けて同じ画面で「アイドル」と検索すると検索結果画面が「ミックスナッツ」のままで更新されないという状態になっていました。
やったこと
原因の究明
Vue Routerにおいてはクエリのみが異なり、パスが同じ場合は同じコンポーネントが再利用されてしまうため、変更が反映されないということが原因だと考えました。
Vue Routerのドキュメントに記載がありました。
ルートのパラメーターを使う際に特筆すべき点は、ユーザーが /user/foo から /user/bar へ遷移するときに同じコンポーネントインスタンスが再利用されるということです。 両方のルートが同じコンポーネントを描画するため、古いインスタンスを破棄して新しいものを生成するよりも効率的です。しかしながら、これはコンポーネントのライフサイクルフックが呼ばれないことを意味しています。
検索ではtermというqueryに検索ワードを入れて検索結果を表示するという処理にしてあります。
今回の例だと、https://credits-spot.vercel.app/recordings?term=ミックスナッツ
→ https://credits-spot.vercel.app/recordings?term=アイドル
とURLを変更しても同じコンポーネントが使われてしまうということですね。
ナビゲーションガードを使う
こちらに以下の記載がありました。
パラメータまたはクエリの変更は enter/leave ナビゲーションガードをトリガーしない ということを覚えておいてください。それらの変更に対応するために $route オブジェクトを監視する、またはコンポーネント内ガード beforeRouteUpdate を使用するかの、どちらかができます。
今回はbeforeRouteUpdate
を使用することにしました。
onBeforeRouteUpdate((to, from, next) => { recordingTerm= (to.query.term as string) || '' # recordingTermは検索ワード currentPage.value = 1 onClickHandler(currentPage.value, recordingTerm).then(() => { # onClickHandlerは検索結果を1ページ分に表示する関数 next() }) })
recordingTerm= (to.query.term as string) || ''
でqueryの値(=検索ワード)を取得してrecordingTermに代入し、onClickHandler(検索結果を表示する関数)を呼び出しています。
しかし、検索結果画面は更新されませんでした。
検索ワードをリアクティブにする
検索ワードであるrecordingTermがリアクティブ変数になっていなかったことが原因でした。以下のように修正したら無事画面が更新されるようになりました!
const recordingTerm = ref((route.query.term as string) || '') recordingTerm.value = (to.query.term as string) || ''`
リアクティブにしないとDOMが自動的に更新されない、というのがすっかり抜けていました…
まとめ
Vue Router使用時に、queryが変更されたらページが更新されるようにしたい場合は以下の対応を行えば良いかと思います。
beforeRouteUpdate
を使用する- 更新したいものをリアクティブにする
自作サービスで初めてComposition APIに触れたのですが、リアクティブについて理解を深めることができて良い経験になりました。