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を有効にしてみてはいかがでしょうか。