/stores/todos.ts を以下の内容で作成します。
これは次のような機能を持ったストアになります
・変数todos にTODOリストを保存
・todoOrderedDesc() でID大きい順に並べ替えてリストを返す
・addTodo() でリストの最後にメンバーを追加する
・changeName() でidで指定したメンバの名前を変更する
記述方式はOptions APIライク(Vuexに似た昔ながらの書き方)でも、Composition APIライク(Vue3の書き方)でも
どちらでも記述することができます。
ここでは Composition API ライクで記述してみます。
stores/todos.ts
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import _ from 'lodash'
interface Todo {
id: number
name: string
}
export const useTodoStore = defineStore('todo', () => {
const todos = ref<Todo[]>([
{
id: 1,
name: 'TODOその1'
},
{
id: 2,
name: 'TODOその2'
}
])
const todoOrderedDesc = computed(() => {
return _.sortBy(todos.value, 'id').reverse()
})
function addTodo(newTodo: Todo) {
todos.value.push(newTodo)
}
function changeName(id: number, name: string) {
todos.value = todos.value.map((v) => {
return v.id === id
? {
id: id,
name: name
}
: v
})
}
return { todos, todoOrderedDesc, addTodo, changeName }
})
import { storeToRefs } from 'pinia'
import { useTodoStore } from '../stores/todos'
const todoStore = useTodoStore()
const { todos } = storeToRefs(todoStore)
storeToRefs で todos を ref にすることでリアクティブに扱うことができます。
<template>
<ul>
<li v-for="todo in todos" :key="todo.id">
<div>{{ todo.id }} : {{ todo.name }}</div>
</li>
</ul>
</template>
/stores/todos.ts に定義した更新メソッドを呼び出して値を更新します
todoStore.changeName(1, 'TODOその1 ● 変更')
(注意)直接値を更新することもできてしまいます https://github.com/vuejs/pinia/issues/58
todos.value = []
const { todos } = storeToRefs(todoStore)
↓ 次のように修正して readonly にします
const { todos: todosMutable } = storeToRefs(todoStore)
const todos = readonly(todosMutable)
これでこのコンポーネント内では「値の変更やデータの追加、削除などができない事が担保された状態」で扱うことができます。
/stores/todos.ts で定義した更新メソッドもこのコンポーネント内では実行することができなくなります
/stores/todos.ts を以下のように修正します
return { todos, todoOrderedDesc, addTodo, changeName }
↓
return { todos: readonly(todos), todoOrderedDesc, addTodo, changeName }
これで
// これは以下のエラーによって実行できなくなります
// [Vue warn] Set operation on key "value" failed: target is readonly.
todos.value = []
この方法だと更新メソッドは問題なく使用することができます。
https://pinia.vuejs.org/core-concepts/state.html#subscribing-to-the-state
Vuexのsubscribeメソッドと同様に、ストアの$subscribe()メソッドを通じて、状態とその変化を監視することができます。通常の watch() よりも $subscribe() を使う利点は、サブスクリプションがパッチの後に一度だけ起動することです(例:上記の関数版を使用した場合)。
例:userStore の 値を監視してアイコンを変更する
userStore.$subscribe((mutation, state) => {
changeIcon(state.user.iconURL)
});
https://pinia.vuejs.org/core-concepts/actions.html#subscribing-to-actions
store.onAction()でアクションとその結果を観察することが可能です。これに渡されたコールバックは、アクション自体の前に実行されます。 after handle promisesは、アクションが解決した後に関数を実行することができます。
onErrorでは、アクションがスローまたはリジェクトされた場合に関数を実行することができます。これらは、Vueのドキュメントにあるこのヒントと同様に、実行時にエラーを追跡するのに便利です
要約すると
・$onAction は全てのアクション実行前に呼ばれます。(何かのアクション実行前に毎回呼ばれます)
・特定のアクションにのみ紐付けたい場合は、引数の name からアクション名で判断させます。
・特定のアクションの実行後に関数を実行したい場合は after() を利用します
例:userStore の deleteUser() 実行完了後に 関数deleteAllPosts() を実行させたい場合
const unsubscribe = userStore.$onAction( ({ name: actionName, store, after }) => {
if (actionName === "deleteUser") {
after(() => {
deleteAllPosts();
});
}
}
);
コンポーネント内に 以下のように記述します
watch(
() => myStore.value,
(value) => {
console.log("===== changed ! =====");
console.log(value);
}
);
watchの 第一引数は myStore.value ではなくて () => myStore.value と記述します
pinia-plugin-persistのインストール
npm install pinia-plugin-persist
main.ts
import { createPinia } from "pinia";
import piniaPersist from "pinia-plugin-persist";
const pinia = createPinia();
pinia.use(piniaPersist); // ← 追加
app.use(pinia);
store/xxxx.ts
persist: {
enabled: true
}