VueのCompositionAPIでPiniaを使う

久しぶりにVueを触っていたら、最近のスタンダードがVuexからPiniaに代わったということで少し試してみました。
以下に紹介してあるコードを少し修正しています。特にgettersを追加してみました。
[BUILD A TO-DO LIST APP WITH PINIA AND VUE 3](https://developers.deepgram.com/blog/2022/04/build-a-todo-list-with-pinia-and-vue-3/)


[Vue公式サイト](https://v3.ja.vuejs.org/)
[Pinia公式サイト](https://pinia.vuejs.org/)



# コマンド

開発は以下のようなコマンドで行えます。

```
vue create todo
cd todo
npm install pinia
npm run serve
```


# 実行画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61915/ed7118b1-bbde-5238-c6ac-56caf1678ea6.png)


# ソースコード
ソースコードは以下の5ファイルになります。

1.  main.js
2.  store/useTodoListStore.js
3.  App.vue
4.  components/TodoList.vue
5.  components/TodoForm.vue


## 1.main.js
**main.js**ではuse(createPinia())でPiniaの使用を宣言します。

```javascript:main.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'

createApp(App).use(createPinia()).mount('#app')
```


## 2.useTodoListStore.js
**useTodoListStore.js**では**defineStore**によって**Pinia**のstoreの定義を行います。
state,getters,actionsの定義を行っていますが、Piniaではmutationsが不要であることが大きな特徴となっています。

```javascript:store/useTodoListStore.js
import { defineStore } from 'pinia'

export const useTodoListStore = defineStore('todoList', {
  state: () => ({
    todoList: [],
    id: 0,
  }),
  getters: {
    doubleCount: (state) => state.todoList.length*2
  },
  actions: {
    addTodo(item) {
      this.todoList.push({ item, id: this.id++, completed: false })
    },
    deleteTodo(itemID) {
      this.todoList = this.todoList.filter((object) => {
        return object.id !== itemID
      })
    },
    toggleCompleted(idToFind) {
      const todo = this.todoList.find((obj) => obj.id === idToFind)
      if (todo) {
        todo.completed = !todo.completed
      }
    },
  },
})

```


## 3.App.vue
**App.vue**は２つのコンポーネントのための枠組みを与えているだけです。


```vue:App.vue
<template>
  <div>
  <TodoList />
  <TodoForm />
  </div>
</template>

<script>
import TodoList from "@/components/TodoList";
import TodoForm from "@/components/TodoForm";

export default {
  name: "TodoApp",
  components: {
    TodoList, TodoForm
  },
};
</script>

```


## 4.TodoList.vue

**TodoList.vue**は現在のtodoリストのリスト表示を行います。


```vue:components/TodoList.vue
<template>
<div>
  <div v-for="todo in todoList" :key="todo.id">
    <div>
      <span :class="{ completed: todo.completed }">{{ todo.item }}</span>
      <span @click.stop="toggleCompleted(todo.id)">&#10004;</span>
      <span @click="deleteTodo(todo.id)">&#10060;</span>
    </div>
  </div>
  <div>
    <span>2重カウント： {{ doubleCount }}</span>
   </div>
</div>
</template>

<script>
import { useTodoListStore } from "@/store/useTodoListStore";
import { storeToRefs } from "pinia";
export default {
  setup() {
    const store = useTodoListStore();
    const { todoList, doubleCount  } = storeToRefs(store);
    const { toggleCompleted, deleteTodo } = store;

    return { todoList, doubleCount, toggleCompleted, deleteTodo };
  },
};
</script>

<style>
.completed {
  text-decoration: line-through;
}
</style>
```

useTodoListStoreでstoreにアクセスします。
```
import { useTodoListStore } from "@/store/useTodoListStore";
---
 setup() {
---
     const store = useTodoListStore();
```

リアクティブな値を得るために**storeToRefs**を使うことに注意してください。ここではstateのtodoListとgettersのdoubleCountを取得しています。

```
    const { todoList, doubleCount  } = storeToRefs(store);
```

actionsの関数はリアクティブではないので、storeからそのまま取得しています。

```
    const { toggleCompleted, deleteTodo } = store;
```


## 5.TodoForm.vue

**TodoForm.vue**はtodoを追加するためのフォームです。

```vue:components/TodoForm.vue
<template>
  <form @submit.prevent="addItemAndClear(todo)">
    <input v-model="todo" type="text" /><button>Add</button>
  </form>
</template>

<script>
import { useTodoListStore } from '@/store/useTodoListStore'
import { ref } from "vue";
export default {
  setup() {
    const todo = ref('')
    const store = useTodoListStore()

    function addItemAndClear(item) {
        if (item.length === 0) {
            return
        }
        store.addTodo(item)
        todo.value = ''
    }

    return { todo, addItemAndClear }
  },
}
</script>
```

useTodoListStoreでstoreにアクセスします。
```
import { useTodoListStore } from '@/store/useTodoListStore'
---
  setup() {
---
    const store = useTodoListStore()
```

特にCompositionAPIの特徴ですが、setup関数内で**ref**を使ってリアクティブな変数todoを宣言します。todoはこのコンポーネントで使われる変数です。またsetup関数内で関数addItemAndClear()を定義して利用できるようにしています。関数addItemAndClear()内ではstore.addTodo()で直接actions関数を呼んでいます。

```
import { ref } from "vue";
---
  setup() {
---
    const todo = ref('')
---
    function addItemAndClear(item) {
        if (item.length === 0) {
            return
        }
        store.addTodo(item)
        todo.value = ''
    }
```