Vue3はTypeScriptとの親和性も高く、React,svelteにも似た書きやすさ。 vue3アプリの初期化( npm init vue@3 )

Vue3のステート管理 Pinia まとめ

● 1. Piniaのデータ保存場所の作成

/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 }
})

● 2. Piniaストアのデータの読み取り

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>

● 3. Piniaストアの値の更新

/stores/todos.ts に定義した更新メソッドを呼び出して値を更新します

  todoStore.changeName(1, 'TODOその1 ● 変更')

(注意)直接値を更新することもできてしまいます https://github.com/vuejs/pinia/issues/58

  todos.value = []

● 4. Piniaストア値のreadonly

1. 完全な readonly

const { todos } = storeToRefs(todoStore)

  ↓ 次のように修正して readonly にします

const { todos: todosMutable } = storeToRefs(todoStore)
const todos = readonly(todosMutable)

これでこのコンポーネント内では「値の変更やデータの追加、削除などができない事が担保された状態」で扱うことができます。
/stores/todos.ts で定義した更新メソッドもこのコンポーネント内では実行することができなくなります

2. Piniaストア値をコンポーネントで直接編集させないようにする readonly

/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 = []

この方法だと更新メソッドは問題なく使用することができます。

● Pinia ストアの値の監視( $subscribe , $onAction , watch)

・$subscribe を使ってストアの値を監視する

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)
});

・$onAction を使ってストアの値を監視する

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 を使ってストアの値を監視する

コンポーネント内に 以下のように記述します

watch(
  () => myStore.value,
  (value) => {
    console.log("===== changed ! =====");
    console.log(value);
  }
);

watchの 第一引数は myStore.value ではなくて () => myStore.value と記述します

● Piniaステートの永続化

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
  }

https://seb-l.github.io/pinia-plugin-persist/

No.2248
03/02 17:08

edit