watch
subscription via a getter
This utility supports subscribing to multiple proxy objects (unlike subscribe
which listens to only a single proxy). Proxy objects are subscribed with a get
function passed to the callback.
Any changes to the proxy object (or its child proxies) will rerun the callback.
Also note the callback will run once immediately when watch
is called, even if the proxies have not been yet mutated, to establish the initial subscriptions.
import { proxy } from 'valtio'
import { watch } from 'valtio/utils'
const userState = proxy({ user: { name: 'Juuso' } })
const sessionState = proxy({ expired: false })
watch((get) => {
// `get` adds `sessionState` to this callback's watched proxies
get(sessionState)
const expired = sessionState.expired
// Or call it inline
const name = get(userState).user.name
console.log(`${name}'s session is ${expired ? 'expired' : 'valid'}`)
})
// 'Juuso's session is valid'
sessionState.expired = true
// 'Juuso's session is expired'
cleanup
You may return a cleanup function that runs both:
- Before each re-invocation of the callback (i.e. due to a watched proxy changing)
- When the
watch
itself is stopped (by calling the cleanup function returned bywatch
)
watch((get) => {
const expired = get(sessionState).expired
const name = get(userState).user.name
console.log(`${name}'s session is ${expired ? 'expired' : 'valid'}`)
return () => {
if (expired) {
console.log('Cleaning up')
}
}
})
// Output from 1st immediate invocation of the callbcak
// 'Juuso's session is valid'
// Changing a dependency will invoke the cleanup callback first,
// but the captured `expired` is false, so we only see output from the
// 2nd invocation of the callback.
sessionState.expired = true
// 'Juuso's session is expired'
// Changing a dependency will again invoke the cleanup callback,
// and `expired` is true now, so we output from both our cleanup
// function as well as the 3rd invocation of the callback.
setTimeout(() => {
userState.user.name = 'Anonymous'
}, 200)
// 200ms -> 'Anonymous's session is expired'
// 'Cleaning up' logged
gotchas
If you remove the setTimeout in the example above, 'Juuso's session is expired' will become 'Anonymous's session is expired', logged twice. Valtio will batch updates by default. You may pass {sync: true}
as a second argument to watch
to disable batching.
no usage tracking
watch
currently does not implement the usage tracking of useSnapshot
, so the callback will rerun anytime a watched proxy (or child proxy) has any fields mutated, regardless of the fields accessed within the callback
's code.
And the return value of get
is just the proxy itself, not a snapshot.
This is because watch
is a vanilla primitive built on top of subscribe
. It is potentially possible for watch
, or a new watch
-like method, to implement usage tracking in the future, see this discussion if interested.