PropSyncのすすめ～Nuxt.jsでモーダルウィンドウを作るときは、PropSyncを使うべし

Nuxt.jsでwebアプリケーションを作ってくると必ずといっていいほど出会う、モーダルウィンドウ。
モーダルウィンドウをただ開閉するだけなのに、Propで子コンポーネントであるモーダルコンポーネントに値を渡したり、
かと思えば、今度はEmitで親コンポーネントに値を渡したり。

もういやっと思って、vuexのstateを使おうとすると上司にコードレビューの時に
「こんなもんにstateに値ぶっこむんじゃねえ」って怒られたり、、、、、、

##PropSyncを使えば良かった

解決策はそう、「PropSyncを使う」それに尽きます。
今回はモーダルウィンドウを例にその使い方を説明していきます。

今回は、pluginsとして”nuxt-property-decorator”を使用したclass moduleの記載方法で
説明していきます。
vue.jsは色々、書き方がありますが、皆さんご自分のプロジェクトに置き換えて
みていただければと思います。
ちなみにUI frameworkはVuetify.jsです。

はい、それでは本題☆

まず、試しにPropだけを使用した場合。

```vuejs:components/testPropdialog.vue
<template>
  <div class="text-center">
    <v-dialog v-model="dialog" width="500">
      <v-card>
        <v-card-title class="text-h5 grey lighten-2">
          Privacy Policy
        </v-card-title>

        <v-card-text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </v-card-text>

        <v-divider></v-divider>

        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn color="primary" text @click="dialog = false"> I accept </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'nuxt-property-decorator'

@Component({})
export default class extends Vue {
  @Prop(Boolean) dialog!: boolean
}
</script>
```

これを使ってモーダルウィンドウ開き、モーダルを閉じるとこんなエラーがでてきます。

>vue.runtime.esm.js?2b0e:619 [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "dialog" found in

Propの値を子側から直接変更するんじゃねえって！！怒られてます。
もしPropを使う場合は、Emitも使わなければいけないので、ちょっと面倒です。

こう言う場合は、PropSyncを使用するともの凄く便利です。
下記がサンプルコードです。

```vuejs:components/testPropsSyncDialog.vue
<template>
  <div class="text-center">
    <v-dialog v-model="dialog" width="500">
      <v-card>
        <v-card-title class="text-h5 grey lighten-2">
          Privacy Policy
        </v-card-title>

        <v-card-text>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </v-card-text>

        <v-divider></v-divider>

        <v-card-actions>
          <v-spacer></v-spacer>
          <v-btn color="primary" text @click="dialog = false"> I accept </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</template>
<script lang="ts">
import { Component, PropSync, Vue } from 'nuxt-property-decorator'

@Component({})
export default class extends Vue {
  @PropSync('parentDialog', { type: Boolean }) dialog!: boolean
}
</script>
```

@PropSync('parentDialog', { type: Boolean }) dialog!: boolean
の部分がPropSyncの書き方です。
dialogの部分がコンポーネントの開閉に使われるboolean値になり、
'parentDialog'部分が親側での値のやり取り使われる変数となります。
そのため、このモーダルコンポーネントを親側で使用する場合は、
.sync修飾子をつけることで、双方向の情報のやり取りが可能となります。

```vuejs:pages/test.vue
<template>
  <v-container>
    <div>Dialogを使用したPropSyncの検証</div>
    <div>
      <v-btn
        @click="propSyncDialogStatus = true"
        class="red lighten-2 focus:outline-none"
        >Click me!</v-btn
      >
    </div>
    <div>
      Propだけの場合。
    </div>
    <div>
      <v-btn
        @click="propDialogStatus = true"
        class="blue lighten-2 focus:outline-none"
        >Click me!</v-btn
      >
    </div>
    <TestPropSyncDialog :parentDialog.sync="propSyncDialogStatus" />
    <TestPropDialog :dialog="propDialogStatus" />
  </v-container>
</template>
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import TestPropSyncDialog from '~/components/testPropSyncDialog.vue'
import TestPropDialog from '~/components/testPropDialog.vue'

@Component({
  components: { TestPropSyncDialog, TestPropDialog },
})
export default class extends Vue {
  propSyncDialogStatus: boolean = false
  propDialogStatus: boolean = false
}
</script>
```

いかがでしたでしょうか？
最初は私もかなり躓きました。
今回の記事がお役に立てれば幸いです。
皆さんもぜひ挑戦してみてください。

私はまだまだ素人に毛が生えた程度の駆け出しエンジニアです。
本記事の誤り等ございましたら、ご教示いただけますと泣いて喜びます。