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:
- Due to the way
useSnapshot
uses a proxy to recorded accesses afteruseSnapshot
has returned, the fields listed inuseDebugValue
are technically from the previous render. - 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.