1. API
  2. useSnapshot

useSnapshot

Create a local snapshot that catches changes. This hook actually returns a wrapped snapshot in a proxy for render optimization instead of a plain object, which is what snapshot() returns.

Rule of thumb: read from snapshots in render function, otherwise use the source.

The component will only re-render when the parts of the state you access have changed; it is render-optimized.

function Counter() {
  const snap = useSnapshot(state)
  return (
    <div>
      {snap.count}
      <button onClick={() => ++state.count}>+1</button>
    </div>
  )
}

Read only what you need

Every object inside your proxy also becomes a proxy (if you don't use ref()). So you can also use them to create a local snapshot.

function ProfileName() {
  const snap = useSnapshot(state.profile)
  return <div>{snap.name}</div>
}

Gotchas

Beware of replacing the child proxy with something else, breaking your snapshot. You can see here what happens with the original proxy when you replace the child proxy.

console.log(state)
{
  profile: {
    name: 'valtio'
  }
}
childState = state.profile
console.log(childState)
{
  name: 'valtio'
}
state.profile.name = 'react'
console.log(childState)
{
  name: 'react'
}
state.profile = { name: 'new name' }
console.log(childState)
{
  name: 'react'
}
console.log(state)
{
  profile: {
    name: 'new name'
  }
}

useSnapshot() depends on the original reference of the child proxy so if you replace it with a new one, the component that is subscribed to the old proxy won't receive new updates because it is still subscribed to the old one.

In this case, we recommend one of the approaches below. In neither example do you need to worry about re-renders because it is render-optimized.

const snap = useSnapshot(state)

return <div>{snap.profile.name}</div>
const { profile } = useSnapshot(state)

return <div>{profile.name}</div>

Dev Mode Debug Values

In dev mode, useSnapshot uses React's useDebugValue to output a list of fields that were accessed during rendering, i.e. which specific fields will trigger re-render when the tracking proxy changes.

There are two disclaimers to the debug value:

  1. Due to the way useSnapshot uses a proxy to recorded accesses after useSnapshot has returned, the fields listed in useDebugValue are technically from the previous render.
  2. Object getter and class getter calls are not included in the useDebugValue output, but don't worry, they are actually correctly tracked internally and correctly trigger re-renders when changed.

Codesandbox demo