Vue初心者がコードレビューで学んだ$emitの役割

プログラミング
この記事は約4分で読めます。
スポンサーリンク

会社で実装を行う上でコードレビューを実施しますが、先日Vueのコードをレビューしていた時に気付きがあったので備忘録として残します。

作ろうとしたもの

とある開発で検索機能を作成しようとした際、以下のようなものを作ろうとしました

検索バーに入力値が入ったら結果画面が表示され(Googleの検索みたいな感じ)、入力値が空になったら結果画面を非表示にする

検索結果にCloseボタンが付いており、Closeボタンを押すことでも検索結果を非表示にすることが出来る。

というものです。

検索ワードの状態によって検索結果の表示・非表示が変わるため、

親 → 検索バー、 子 → 検索結果

という親子構造を想定して実装を進めました。

私の実装(誤った方法)

最初に行った実装は以下の通りです。

実装の理由

「文字が入力されたら結果画面を表示する」という仕様であるため親の状態を常に子に送ります。

閉じるボタンが押されると結果画面は消えるため、子コンポーネントからpropsで送られてきたisShowをfalseに変えようとしたところ、

[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.

というWarningが出てしまい、推奨されない方法でした。

というわけで閉じるボタンが押された場合は isClose というフラグを使用して、isShowを書き換えた場合と同じ挙動になるようにしました。

isCloseをCloseボタンが押されたかを管理するフラグとすると、isShowは検索ワードが存在するかというフラグという解釈にもなります。

  • 検索ワードに文字が存在している場合 
  • 検索ワードに文字がない場合
  • 検索ワードに文字が存在しているが、閉じるボタンを押されている場合

の3つの状態を管理するには1bitのフラグでは足りず、最低でも2bit分のフラグが必要なのだと思っていました。

提案された実装

レビューを踏まえて提案された実装はこちらです。

納得できなかった理由

この提案を初めに聞いた時、納得いきませんでした。

子が親のデータを書き換えるのはおかしい」というのが自分の基本となる考えでした。

自分の中では、$emitは親の関数を実行するもの という認識で、装上どうしても子から親に何かをしなければならない場合の最終手段くらいのもので、あまり多用すべきではないという勘違いをしていたためです(親の関数を実行する != 子が親のデータを書き換える なのに…)

ただこれは$emit の本当の意味を理解していなかったため起きた認識違いでした。

$emitと$parentの勘違い

「子が親のデータを書き換えるのはおかしい」という方法は $parent を使ったデータの更新の方法のことでした。

Vue.jsでコンポーネントの親子間でデータのやり取りを行う方法 - Qiita
最近Vue.jsを使ったプロジェクトに手を出しています。 そこでコンポーネント間で値をやり取りする方法を調べていましたが、 こうするべきでしょ、というのを私なりに探っていった結果をまとめました。 こちらのコードの例で使用しているVu...

$emit は親のコンポーネントに用意されたイベントを発火させるものです。

$parent を使う方法だと子が勝手に親のデータを書き換えてしまうため親が管理できていないことがわかりますが、$emitを使うと子が親に値の変更を伝えて親が値を更新をするため親がデータを管理できていることになります。

親子構造は 1対1の関係ではなく、1対多の関係になることもあるため、今回の1対1のケースでは影響は少ないですが、拡張性を考えると親がデータを管理することで実装がしやすくなる事が多いです。

まとめ

Vueで親子構造を作成するにあたり、今回の学びは

  • 子のデータは基本的に親が管理したほうが良い
  • $emitは親のイベントを発火させるものであって、親のデータを書き換えるものではない
  • $parentでのデータの更新はすべきではない

ということでした。

プログラムを書く上でデータの持たせ方、実装の設計は最後まで悩ましい問題になりますが、今回の学びを今後の設計の一助にしていきたいと思います。

ここまで読んで頂きありがとうございました!

コメント

タイトルとURLをコピーしました