この記事はモバイルファクトリー Advent Calendar 2020 9日目の記事です。
こんにちは、ブロックチェーンチームの新卒エンジニアid:charinesです。
Nuxt.jsにおけるasyncDataの役割
ブロックチェーンチームでは、Nuxt.jsのサーバーサイドレンダリング機能を用いた開発を行っています。asyncData
はページの読み込み時に、返されたPromiseの値をコンポーネントのdata
にマージするためのフックで、ページの移動やエラーページの表示はPromiseの解決を待って行われます。
問題
asyncData
はページコンポーネント毎に定義されるため、読み込み時のエラーハンドリングなどの処理が全ページで共通であったとしても、各ページコンポーネントにその処理を記述しなければなりません。
具体例として、サーバーサイドで実行されたasyncData
内で例外が発生した場合にエラーページを表示するには、asyncData
の第一引数のオブジェクトに定義されたerror
関数を呼び出す必要があります。次に示すのはasyncData
内でAPIからエラーレスポンスが返された際にエラーページを表示する処理です。
Vue.extend({ async asyncData({ app, error }) { try { // app.$api.getUser() が返すプロミスは // statusとmessageをプロパティとして持つ例外でリジェクトされることがある const user = await app.$api.getUser(); return { user }; } catch (err) { if (process.server) { // サーバーサイドで実行されている場合はerrorを呼び出してエラーページを表示する error({ statusCode: err.response ? err.response.status : 500, message: err.message, }); return; } throw err; } }, data: () => ({ user: undefined, }), });
この例ではapp.$api.getUser
という非同期関数の解決した値をuser
としてdata
にマージします。
ここで、このコードを修正して「クライアントサイドでステータスコード401のHTTPレスポンスを受け取った例外が発生した場合はリロードする」という処理を入れることにしました。しかしこのような変更を行う場合、先述の通りasyncData
はページコンポーネント毎に定義されているため、全てのページコンポーネントに修正を行う必要があります。*1
やったこと
全てのページで共通するasyncData
のエラーハンドリングを一箇所のコードにまとめるために、asyncData
を生成する関数を書きました。以下が実際のコードです。
export function createAsyncData(asyncData) { return async (context) => { try { // asyncDataはページ固有の処理を行う関数 const data = await asyncData(context); return data; } catch (err) { const statusCode = err.response ? err.response.status : 500; if (process.server) { context.error({ statusCode, message: err.message, }); return; } if (statusCode === 401) { location.href = context.route.path; // リダイレクトが完了するまでにエラーページが描画されないようプロミスを待機させる await Promise.race([]); } throw err; } }; }
この関数はページ固有の処理を行う関数を引数として受け取り、受け取った関数の実行とエラーハンドリングを行う新たな関数を返します。各ページコンポーネントではこの関数を以下のように使用します。
Vue.extend({ asyncData: createAsyncData(async ({ app }) => { const user = await app.$api.getUser(); return { user }; }), data: () => ({ user: undefined, }), });
これでエラーハンドリングなどの全ページで共通の処理をページコンポーネント毎に書く必要がなくなり、変更が容易なコードになりました。
まとめ
asyncData
関数を生成する関数を書いて全ページで共通の処理をページコンポーネントの実装から分離することで、この関数を修正するだけで全ページのエラーハンドリングを修正することができるようになりました。
明日の記事は id:tsukumaru さんです!