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>