Open6

状態管理ライブラリvaltioのメモ(Vanilla・小規模向け)

Yuichiroh AraiYuichiroh Arai

AstroでVue.js/Reactを使わない環境でvaltioをお試し中です。
状態の更新を従来のオブジェクト操作のように扱えるので親しみやすさがあって気に入っていますが、その一方で仕組みをちゃんと理解して使いこなすのに時間が掛かりそうだなとも感じました。
特に複数の状態をProxyオブジェクトでどうやって管理するのか、一つにまとめるのか複数に分割するのかなどが悩みどころなんですよね。

ちなみに、同作者の jotaiも使ってみたけど、こちらはシンプルに1つの状態を1つのAtomと呼ばれる単位で管理するので使い方で悩むことは特になかったです。
Vanilla・小規模向けという意味でも相性は良いですよね。

というわけで、使いこなせなかったらjotaiに移行することも視野に入れつつ、もう少しだけvaltioを試そうかなと思っています。
Vue.jsのリアクティビティーAPIとの比較も交えつつ不定期でメモしていきます。

Yuichiroh AraiYuichiroh Arai

Proxyオブジェクト

import { proxy } from 'valtio/vanilla'
const obj = { count: 0 }
const $state = proxy(obj)
$state.count++

valtioのproxy関数を呼び出すとProxyオブジェクトが返ってきます(Vue.jsのreactiveと同じですね)。
Proxyであることを除けば、渡したオブジェクトと表面的な構造は変わらないので、上記の例で言うとobjと$stateの型は同じになります。
これらについては、操作しようとしている対象がリアクティブなオブジェクトなのかが、直観的に分かりづらくはなりますね。

例えば、Vue.jsのRefであれば、

const $count = ref(0)

$countはRef<number>のような型になるので、IDEのツールチップなどで確認が簡単です。

今後、単にProxyオブジェクトと表記してある場合は、valtioによってリアクティブな処理がされているものを指すこととします。

Yuichiroh AraiYuichiroh Arai

監視

valtioの監視はやり方が複数あるので使い分けが重要になります。

  • subscribe
  • subscribeKey
  • watch
Yuichiroh AraiYuichiroh Arai

subscrbe

https://valtio.dev/docs/api/advanced/subscribe

import { proxy, subscribe } from 'valtio/vanilla'

const $states = proxy({ count1: 0, count2: 0 })
const unsubscribe = subscribe($states, () => {
  console.log($states.count1, $states.count2)
})

$states.count1++
$states.count2++

// 監視停止
// unsubscribe()

// console.log(1, 1)
  • 監視対象のProxyオブジェクトは1つだけ
  • どのプロパティを変更してもコールバック関数は呼び出される
  • 監視を停止するにはsubscribeの戻り値を使用する
  • デフォルトでは、コールバック関数の呼び出しは遅延されます
    • 複数のプロパティが変更された場合、コールバック関数の呼び出しは1回にまとめられます
    • プロパティ変更の直後にunsubscribeを実行すると、コールバック関数が呼び出される前に監視が停止されます

コールバック関数の非同期/同期呼び出し

第3引数によってコールバック関数の呼び出しタイミングを切り替えられます。

import { proxy, subscribe } from 'valtio/vanilla'

const $states = proxy({ count1: 0, count2: 0 })
const unsubscribe = subscribe(
  $states,
  () => {
    console.log($states.count1, $states.count2)
  },
  true,
)

$states.count1++
// console.log(1, 0)
$states.count2++
// console.log(1, 1)

// 監視停止
unsubscribe()

第3引数をtrueにすると、プロパティの変更があるたびにコールバック関数が呼び出されます。
上記の例では、コールバック関数が2回呼び出されてから監視が停止します。

入れ子のオブジェクト

入れ子のオブジェクトの一部だけを監視対象にすることもできます。

import { proxy, subscribe } from 'valtio/vanilla'

const $states = proxy({ count1: { value: 0 }, count2: { value: 0 } })
subscribe($states.count1, () => {
  console.log($states.count1.value)
})

$states.count1.value++

上記の例では$states.count2.valueが変更されてもコールバック関数は呼び出されません。

Yuichiroh AraiYuichiroh Arai

subscribeKey

https://valtio.dev/docs/api/utils/subscribeKey

特定のプロパティのみを監視対象にできます。
これにより、不必要なコールバック関数呼び出しを避けることができます。

import { proxy } from 'valtio/vanilla'
import { subscribeKey } from 'valtio/vanilla/utils'

const $states = proxy({ count1: 0, count2: 0 })
const unsubscribeKey = subscribeKey($states, 'count1', () => {
  console.log($states.count1)
})

// コールバック関数は呼び出される
$states.count1++

// コールバック関数は呼び出されない
// $states.count2++

// 監視停止
// unsubscribeKey()

個人的には、これがないとvaltioを使う理由がなくなってしまうぐらい重要な機能でした。
不必要なコールバック関数呼び出しを避けるためには、複数の状態に対してProxyオブジェクトを1つずつ用意することになり、結局はjotaiを使うのとあまり変わらなくなってしまうからですね。