1. Basic
  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 compared to snapshot() method. Rule of thumb: read from snapshots, mutate 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>
	)
}

[Notes] Every object inside your proxy also becomes a proxy (if you don't use "ref"), so you can also use them to create the local snapshot as seen on example B.

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

Beware that you still can replace the child proxy with something else so it will break your snapshot. You can see above 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 the example C or D. On both examples you don't need to worry with re-render, because it is render-optimized.

const snap = useSnapshot(state)

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

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

Codesandbox demo