Vue
で使える状態管理のライブラリ Pinia
を試してみたのでメモです。
アイコンかわいい ! ドキュメントは コチラ
注意
この記事を書いている時点(2021/04/04) ではまだalpha
版です!!
インストール
npm install pinia@next
Vue2 でも使えるみたいですがその場合は
pinia@latest
と @vue/composition-api
をインストールすれば使えるようです。(試していません。)
app に Pinia を追加する
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import { createPinia } from "pinia";
createApp(App)
.use(router)
.use(createPinia()) //追加
.mount("#app");
Store の定義
import { defineStore } from "pinia";
export const useUserStore = defineStore({
//idでストアを特定している。
id: "user",
//stateの定義、初期値を返す関数を定義する。
state: () => ({
firstName: "",
familyName: "",
}),
getters: {
fullName() {
//thisでstateを参照できる。
//補完も有効
return `${this.firstName}・${this.familyName}`;
},
hello() {
//ゲッターも参照できる。
return `Hello! ${this.fullName}`;
},
},
actions: {
setFullName(firstName: string, familyName: string) {
this.firstName = firstName;
this.familyName = familyName;
},
//Promiseを返すコードもかける
async loadUser() {
const user = await requestHoge..//サーバーにリクエストしデータを取得
this.firstName = user.firstName;
this.familyName = user.familyName;
},
},
});
こんな感じになります。
id
でストアを紐づけて管理しているようです。
既存の値をもとに算出したい場合は getters
を使います。
this
を使うことで参照ができるようです。
コンポーネントから使う
<script lang="ts">
import { computed, defineComponent } from "vue";
import { useUserStore } from "./useUserStore";
export default defineComponent({
name: "QiitaSample",
setup() {
const store = useUserStore();
const firstName = computed(() => store.firstName);
const familyName = computed(() => store.familyName);
const fullName = computed(() => store.fullName);
const setFullName = () => {
store.setFullName("太郎", "キータ");
};
const loadUser = async () => {
await store.loadUser();
};
return {
firstName,
familyName,
fullName,
setFullName,
loadUser,
};
},
});
</script>
setup
関数の中で 先ほど定義したuseUserStore
を呼び出すことで使えるようになります。
値を参照する
//stateから
const firstName = computed(() => store.firstName);
const familyName = computed(() => store.familyName);
//gettersから
const fullName = computed(() => store.fullName);
それぞれ computed
を使うことでできます。
値を変更する
変更については様々な方法が用意されているようです。
直接代入
store.firstName = "太郎";
store.familyName = "キータ";
なんと、そのままストアに代入することもできます。
$patch を使う
store.$patch({
firstName: "太郎",
familyName: "キータ",
});
$patch
を使う事でも状態を変更することができます。
state
の変更したい部分だけ(例えばfirstName
のみ) を渡すこともできます。
store.$patch((state) => {
state.firstName = "太郎";
state.familyName = "キータ";
});
このようにstate
を引数とする関数を受け取ることもできます。
$state で全部置き換える
store.$state = {
firstName: "太郎",
familyName: "キータ",
};
$state
に代入することで state
全体を置き換えることもできます。
action を実行する
const setFullName = () => {
store.setFullName("太郎", "キータ");
};
そのまま呼び出すだけです。
const loadUser = async () => {
await store.loadUser();
};
Promise
を返すものは await
を使うこともできます。
複数のストアを組み合わせる
import { defineStore } from "pinia";
export const useHogeStore = defineStore({
id: "hoge",
state: () => ({
hogeName: "",
}),
getters: {
enclosedHogeName() {
return `[${this.hogeName}]`;
},
},
});
import { defineStore } from "pinia";
import { useHogeStore } from "./useHogeStore";
export const useFooStore = defineStore({
id: "foo",
state: () => ({
fooName: "",
}),
getters: {
fooAndEnclosedHoge() {
//ここでHogeStoreを参照する
const hogeStore = useHogeStore();
return `${this.fooName} ${hogeStore.enclosedHogeName}`;
},
},
});
getter
の中で useXxxStore
とすることで別のストアを参照できるそうです。
コードを見た感じ useXxxStore
するたびに buildStoreToUse
が呼ばれているように見えますがパフォーマンスはどうなんでしょね 🤔
別のストアを一つだけ参照するならこんな感じで良いみたいですけど
互いにいろいろ参照する場合は専用のストアを作ったほうが良いとのこと
(このあたり)
import { defineStore } from "pinia";
import { useFooStore } from "./useFooStore";
import { useHogeStore } from "./useHogeStore";
export const useSharedStore = defineStore({
id: "shared",
getters: {
fooAndEnclosedHoge() {
const hogeStore = useHogeStore();
const fooStore = useFooStore();
return `${fooStore.fooName} ${hogeStore.enclosedHogeName}`;
},
},
});
action
も同様です。
まだドキュメントに書いていない機能
$reset
ストアを初期状態にする関数のようです
$subscribe
Vuex
のsubscribe
みたいなものかな?
vuex-persisted-state
のようにストレージに保存したりする用途に使えるかもしれない。
今のところ type
に絵文字が入ってますね😅
さいごに
必要最小限の機能がそろっていて型もついているので便利そうな印象です。
正式リリースが楽しみですね