【JavaScript】ボタン連打されてもAPIリクエストを重複させたくない!
こんにちは!
ラブグラフエンジニアのひろです。
この記事では、トグルボタンをクリックした際にAPIからデータを取得する処理において、連打によるデータ取得の重複問題を解消した経緯と、その方法について書いていきます。
起きていたこと
47都道府県の一覧を表示していて、ユーザーがいずれかの都道府県をクリックすると、その都道府県に関連する市区町村のデータを取得するAPIが走り、結果を一覧表示する。という機能を作っていました。
しかし、このトグルボタンを連打すると、市区町村データの取得処理が同時に複数回走ってしまい、結果としてデータが重複して表示されてしまう問題が発生しました。
富山県の例。射水市~氷見市までが2回表示されている
最初の対策
この問題に対処するため、まずisLoading
というフラグを用意しました。
このフラグがtrue
の間は、新たなAPI呼び出しを行わないようにすることで、連打による重複処理を防ごうとしました。
具体的には、APIを呼び出す関数の最初にisLoading
がtrue
かどうかをチェックし、true
の場合は早期に関数から抜けるロジックを実装しました。
async fetchMunitipalitiesInArea(areaId) {
// 現在取得中か、すでに取得済みのエリアの場合は処理しない
if (this.isLoading || this.municipalities.some(municipality => municipality.areaId === areaId)) {
return;
}
this.isLoading = true;
const response = await fetch(`/api/municipalities?area_id=${areaId}`);
const data = await response.json();
this.municipalities = this.municipalities.concat(data.municipalities);
this.isLoading = false;
},
さらなる問題
しかし、この対策には欠点がありました。
47都道府県全てを表示しているため、異なる都道府県のボタンをほぼ同時にクリックした場合、最初のクリックで isLoading
が true
になり、他の都道府県のデータ取得がブロックされてしまいました。
これは、ユーザー体験を著しく損なうものでした。
最終的な対策
この新たな問題に対応するため、 loadingAreaIds
という配列を用意し、処理中の都道府県のIDをこの配列で管理することにしました。
具体的には、APIを呼び出す際にその都道府県のIDを loadingAreaIds
に追加し、処理が完了したらそのIDを配列から削除します。
async fetchMunitipalitiesInArea(areaId) {
// 取得中のエリアか、すでに取得済みのエリアの場合は処理しない
if (this.loadingAreaIds.includes(areaId) || this.municipalities.some(municipality => municipality.areaId === areaId)) {
return;
}
this.loadingAreaIds.push(areaId); // 取得中のID群に areaId を追加
const response = await fetch(`/api/municipalities?area_id=${areaId}`);
const data = await response.json();
this.municipalities = this.municipalities.concat(data.municipalities);
this.loadingAreaIds = this.loadingAreaIds.filter(id => id !== areaId); // 取得が終わった areaId を取り除く
},
この方法により、特定の都道府県での処理が他の都道府県の処理に影響を与えないようになりました。
終わりに
今回のような、APIの連続呼び出しによる問題は、処理中の状態を細かく管理することで解決できます。
最終的な対策により、ユーザーが異なる都道府県をクリックしても、各都道府県の市区町村データ取得処理が正確に行われ、重複や遅延なく、スムーズなユーザー体験を実現できるようになりました。
Discussion