id 指定して store を作るには?

# はじめに
Vue.js と Pinia をお使いの皆さん!
まったく同じ機能の store を複数作る時って、どうしていますか?
id 以外、中味はまったく同じものをいっぱい作りたいんだけど...? っていう場合です。

そんな疑問を持ってググってみたけど情報が全然なかったので、自分で調べてみました。
やってみると意外と簡単にできたので、以下に御紹介したいと思います。

# 環境構築
事例として簡単なものがよいと思うので、Vue.js の Scaffold から始めてみることにします。

```bash
npm init vue@3
```

以下の通り質問に答えていくと、Vue アプリの雛型が作成されます。
基本的には既定値で良いですが、"Add Vue Router...?" と "Add Pinia...?" については Yes を選択するようにします。

```
Project name: … vue-project
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes  (← Yes を選択)
✔ Add Pinia for state management? … No / Yes                          (← Yes を選択)
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? … No / Yes
```

初期化のために yarn を実行する。

```
cd vue-project
yarn
```

Pinia の追加を選択したので src/stores というフォルダが出来ており、counter.js というファイルが出来ていると思います。

```javascript
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})
```

ただ、これを使ったアプリは作成されないようなので、自分で作ることにします。
おなじみの(?) カウンターのデモアプリです。

src/views に以下の内容のファイル CounterView.vue を置く。

```vuejs
<script setup>
import { useCounterStore } from "../stores/counter";

const store = useCounterStore();
</script>

<template>
  <div>
    <h1>Hello World!</h1>
    <p>Click the button to increment count!</p>
    <div class="box">
      <button type="button" @click="store.increment">count is {{ store.count }}</button>
    </div>
  </div>
</template>

<style scoped>
.box {
  margin-top: 1em;
  margin-left: 2em;
}
</style>
```

次に、このページへのルーティング設定を追加します。

src/router/index.js に以下の設定を追加。

```javascript
   {
      path: '/counter',
      name: 'counter',
      component: () => import('../views/CounterView.vue')
    }
```

src/App.vue に以下の設定を追加。
15行目あたりの "&lt;RouterLink to="/about"&gt;About&lt;/RouterLink&gt;" の下に以下を追加します。

```vuejs
 <RouterLink to="/counter">Counter</RouterLink>
```

これで一応準備が整ったので、アプリが動くことを確認してみます。

```bash
yarn dev
```

ブラウザで <http://localhost:5173/counter> にアクセスし、ボタン押下で数字がインクリメントされれば OK です!

# 課題事項とその対応

ここからようやく本題に入りますが、例えばの話、同じ挙動 (押下時に count をインクリメント) を持つボタンをもう 1 つ追加したいと考えてみます。
もちろんこの時、count のインクリメントは個別にできるようにしなければなりません。

そのためには 下記に示すように、useCounterStore と (id 以外は) まったく同じものをもう 1 つ作ればよいのですが、ボタン増やすたびにそんな事いちいちやってられませんよね? っていう話です。

```javascript
// useCounterStore2 を作成: id 以外は useCounterStore とまったく同じ
export const useCounterStore2 = defineStore('counter2', () => {
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})
```

そこで、先程の counter.js を、id 指定して defineStore() できるようにちょっとひと工夫してみます。
こんな感じになると思います。

```javascript
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'

export const useCounterStore = (id) => defineStore(id, () => {  // ← この行を直しただけ
  const count = ref(0)
  const doubleCount = computed(() => count.value * 2)
  function increment() {
    count.value++
  }

  return { count, doubleCount, increment }
})
```

これを呼び出す方 (CounterView.vue) は以下の通りです。

```javascript
const store = useCounterStore("counter")();
```

注意点として、useCounterStore("counter") だけだと単に関数を定義するというだけの体になるため、これを実行するには上記のように最後に () を付ける必要があります。

以上の修正後に再度動作確認してみると、修正前と動きは変わらないはずだと思います。(この時点ではデグレしてないことが大事!)

そこで次に、ボタンをもう 1 つ追加してみることにします。

ストアの追加はとても簡単で、違う id を指定して useCounterStore を呼び出すだけです。
これは先程コピペで作った useCounterStore2 と同じ効果となります。

```javascript
const store = useCounterStore("counter")();
const store2 = useCounterStore("counter2")();   // ← この行を追加
```

新しいボタンは store2 の方を使うようにします。

```vuejs
  <button type="button" @click="store.increment">count is {{ store.count }}</button>
  <button type="button" @click="store2.increment">count is {{ store2.count }}</button>
```

これで最終的な動作確認をしてみることにします。
各々のボタン押下時に、個別に count がインクリメントされるようになってれば OK です!

ちなみに以下 蛇足ながら、もし同じ id で store 作成を試みた場合どうなるのか?

```javascript
const store = useCounterStore("counter")();
const store2 = useCounterStore("counter2")();
const store3 = useCounterStore("counter")();    // ← store と同じ id を指定
```

これは実際にやってみると分かりますが、store と store3 はまったく同じものを参照する形になります。

```javascript
console.log(store === store2);   // ← false
console.log(store === store3);   // ← true
```

このように、同一の id に対してはシングルトン的な挙動となるようです(まあよく考えたらそりゃそうですよね、という話ではありますが)。

# おわりに

以上、ご参考になりましたら幸いです。
ご精読ありがとうございました。