1. API
  2. watch

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 by watch)
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.