Nuxt.js + Vuex におけるフォームバリデーションのベストプラクティスを考える

// 🙎‍♂️ Yuji Tsuburaya📆 May 04, 2020🔖 NuxtJSVue.js

Nuxt.js + Vuex でフォームバリデーションを実装する機会が増えたので、ベストプラクティスについて考えてみました。いろいろな実現方法があるとは思いますが、自分なりの答えが出たので、記事にしておきたいと思います。VeeValidator 等のライブラリ等は使わない前提です。

Action 層でバリデーションをかける

バリデーションの判定は Action 層で行います。Page 層にはロジックは書かない方針で一貫した設計をしているため、Page 層にはバリデーションロジックは書きません。

Action は、以下のようコードになります。

async createTodo({ dispatch }, form: TodoForm): Promise<void> { assertTodo(form); // 第二引数で渡ってくる form の妥当性をチェック // DO SOMETHING // form -> model // API 通信 // commit 等 }

Vuex では dispatch したときの第二引数としてオブジェクトを渡すことができるので、このオブジェクトに対し、バリデーション判定を行います。具体的なロジックについては割愛します。

Action 層の責務としては、以下の2つになります。

図にすると、以下のような形になります。

バリデーションロジックでは、独自のバリデーションエラーを作って throw する

バリデーションにひっかかった場合(👆の例でいう assertTodo で判定して、ひっかかった場合)には、独自のエラーを発生させ、それを投げることとしています。Error を拡張した独自のエラー ValidationError を自前で定義し、フィールドとして、エラーの内容を詰めるようにしています。

以下は、僕が定義している独自エラーの例です。

export class ValidationError extends Error { errors: Array<ValidationErrorObj>; constructor(errors: Array<ValidationErrorObj>, message: string = 'ValidationError') { super(message); this.errors = errors this.name = 'ValidationError'; } }

バリデーションエラーに引っかかった場合には、以下のようにエラーを throw するようにしています。errors には、エラーに引っかかった要素名と、エラー文言を詰めたオブジェクトの配列を渡すようにしています。

throw new ValidationError(errors);

Page 層でのバリデーションエラーハンドリング

Page から Action の呼び出しは dispatch で行います。dispatch の返り値は Promise なので、例外が投げられた場合の処理を catch ブロックにまとめることが出来ます。

エラーのクラスを instanceof で判定してやって、バリデーションエラーだった場合には、エラーメッセージを出す等の処理を行います。また、想定していない例外(たとえば、API 通信で発生したネットワークエラー等)の場合は、そのまま例外を throw して、Sentry 等に処理を委ねることにしています。

以下はその例です。await/catch で書くのは好み。

const res = await this.$store.dispatch('todo/createTodo', this.form) .catch((e) => { if (e instanceof ValidationError) { this.errors = e.errors; } else { throw e; } });

errors フィールドに、エラーの内容を詰めているので、Page 側で、エラーの内容を表示させたりすることができます。ここはアラート出すなり、ビューに表示するなり、お好みでどうぞ。

ちなみに、 Jest を使ったバリデーションのテストは以下のようになります。Action 単位でテストするのが好みなので、こんな感じで。

await store.dispatch('createTodo', form).catch((e: any) => { expect(e instanceof ValidationError).toBeTruthy(); })

シェアしてくれたら喜びます 🐌

広告

書いた人

___35d

Yuji Tsuburaya (@___35d

👩‍💻 Frontend Engineer

🏢 J-CAT CTO Co-Founder / ex. BizReach

🎨 by @ch1ch1ch1_123

💚 Minimalism / Notion / Figma

📱 Author of @FastNotion

詳しいプロフィールはこちら

広告