Piniaのステート、ゲッター、アクションを使いこなす
以下では、Piniaのステート、ゲッター、アクションに関わる機能を説明していきます。説明のために、スマートフォン機種の名前(name)、会社(vendor)、価格(price)、機能リスト(features)のステートと、価格をカンマ区切りして返却するゲッター(formattedPrice)を含む、リスト9のストアを利用します。
export const usePhoneStore = defineStore('phone', () => {
// ステート
const name = ref('iPhone 15 Pro')
const vendor = ref('Apple')
const price = ref(159800)
const features = ref(['5G', 'チタニウム', 'アクションボタン'])
// ゲッター
const formattedPrice = computed(() => {
const formatter = new Intl.NumberFormat('ja-JP')
return formatter.format(price.value)
})
// 返却
return { name, vendor, price, features, formattedPrice }
})
ステートの参照と更新
ストアのステートを参照・更新する方法について、図4のサンプルで説明します。このサンプルでは、機種、会社、価格のテキストボックスの内容を変更すると画面に反映されます。また、機能を追加・削除することもできます。
まず機種、会社、価格のテキストボックスを含むMutateStateコンポーネントの<template>部は、リスト10の通りです。ストアのステート(name、vendor、price)は、v-modelディレクティブに直接指定できます。この指定により、入力内容とステートが双方向データバインディングで同期します。なお、(1)の「v-model.number」は、store.priceがnumber型である指定です。
<div>機種:<input v-model="store.name"></div> <div>会社:<input v-model="store.vendor"></div> <div>価格:<input v-model.number="store.price"></div> <!--(1)-->
双方向データバインディングではなく、ストアオブジェクトのプロパティとしてステートを直接参照・更新することもできます。機能を追加・削除するMutateFeaturesコンポーネント(リスト11)を例に説明します。ここではstore.featuresプロパティ(配列)に対して、pushメソッドで機能(テキストボックスの入力値であるinputFeature)を配列の最終要素に追加し、popメソッドで配列の最終要素を削除します。store.featuresプロパティが更新されると、その内容が画面表示に反映されます。なおここでは、各処理後にテキストボックスの入力値(inputFeature)を空にしています。
// 追加ボタンの処理
function onClickButtonAddFeature() {
store.features.push(inputFeature.value)
inputFeature.value = ''
}
// 削除ボタンの処理
function onClickButtonDelFeature() {
store.features.pop()
inputFeature.value = ''
}
このようにPiniaのステートは、コンポーネントのデータと同じ方法で参照・更新できます。特別な記述方法は必要ありません。
引数付きゲッター
ここまで紹介したゲッターは、あたかも変数のように参照していましたが、引数を伴うメソッドの形式でゲッターを提供することもできます。この方法を、図4と同一内容を表示するサンプル(p005-getter-with-param)で説明します。このサンプルでは、リスト9のformattedPriceゲッターをもとに、単位を指定できるformattedPriceWithUnitゲッターを、リスト12の通り実装します。computed関数の引数に設定する関数で、ゲッターの値そのものではなく、値を返す関数を返すように実装します。
const formattedPriceWithUnit = computed(() => {
return (unit:string) => `${unit}${formattedPrice.value}`
})
formattedPriceWithUnitゲッターは、<template>部でリスト13の通り参照できます。
{{ store.formattedPriceWithUnit('¥') }}
非同期アクション
アクションには非同期処理を含めることができます。この実装を、図5のサンプルで説明します。ボタンを押すと、phone.jsonをネットワーク経由で取得して、その内容でステートを更新します。
ボタンクリック時に実行されるfetchDataAsyncアクションは、ストアにリスト14の通り実装します。(1)で、fetch関数を実行してphone.jsonを取得し、それをres.jsonメソッドでJavaScriptオブジェクトに変換します。取得したJavaScriptオブジェクトに含まれる値を利用して、(2)でステートを更新します。
async function fetchDataAsync() {
// phone.jsonを取得 ...(1)
var result = await fetch('/phone.json').then(res => res.json())
// ステートを更新 ...(2)
name.value = result.name
vendor.value = result.vendor
price.value = result.price
features.value = result.features
}
なお、取得するphone.jsonは、リスト15の内容をpublicフォルダーに配置します。
{
"name": "Pixel 8 Pro",
"vendor": "Google",
"price": 159900,
"features": ["おサイフケータイ", "5G", "温度計"]
}
まとめ
本記事では、Vue.jsの状態管理ライブラリーPiniaを、Vuexと比較しながら説明しました。Vuexで実現していた状態管理を、よりシンプルかつ自然な形式で実装できるようになり、TypeScriptとの親和性もより高くなりました。
本連載では、Vue.jsをTypeScriptで活用するためのさまざまな技術要素を説明してきました。ViteベースのCLIツールを利用することで、今回紹介したPiniaなどライブラリーを含めて、追加設定を行うことなくTypeScriptを利用できます。Vue.jsのWebページを作成する際、CLIツールでTypeScriptを有効にしてみてはいかがでしょうか。
