snapshot
snapshot
is a vanilla function that takes a proxy and returns an immutable, read-only object representing the current state.
This is the underlying function used by the useSnapshot
hook, but it can also be used directly in non-React environments or for specific use cases in React.
import { proxy, snapshot } from 'valtio/vanilla';
const store = proxy({ name: 'Mika' });
const snap1 = snapshot(store); // An efficient, unproxied copy of the current store
const snap2 = snapshot(store);
// If the state hasn't changed, snapshot returns the same object reference
console.log(snap1 === snap2); // true
store.name = 'Hanna';
const snap3 = snapshot(store);
// If the state has changed, a new snapshot object is created
console.log(snap1 === snap3); // false
How Immutability is Achieved
Immutability is achieved by efficiently deep copying and freezing the object. This process is optimized using a Copy-on-Write mechanism.
Copy-on-Write
Snapshots are created lazily. When you mutate a part of the state, only the mutated object and its ancestors in the state tree are copied for the next snapshot. Unchanged parts of the state tree share the same object references as the previous snapshot.
This makes snapshot creation very fast, as its cost is proportional to the depth of the change, not the size of the entire state.
const author = proxy({
firstName: 'f',
books: [{ title: 't1' }, { title: 't2' }],
});
const s1 = snapshot(author);
// Mutate a nested property
author.books[1].title = 't2b';
const s2 = snapshot(author);
console.log(s1 === s2); // false (root changed)
console.log(s1.books === s2.books); // false (books array changed)
console.log(s1.books[0] === s2.books[0]); // true (book 1 is unchanged)
console.log(s1.books[1] === s2.books[1]); // false (book 2 changed)
Class Instances in Snapshots
Snapshots preserve the prototype of class instances, so methods and getters work as expected. When you call a method or getter on a snapshot, this
refers to the frozen snapshot instance, not the mutable proxy state.
class Author {
firstName = 'f';
lastName = 'l';
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
const state = proxy(new Author());
const snap = snapshot(state);
console.log(snap instanceof Author); // true
state.firstName = 'f2';
// The method call uses the snapshot's state, not the latest state
console.log(snap.fullName()); // 'f l'
Vanilla JavaScript Usage
In vanilla JavaScript, you can read properties directly from the proxy. However, snapshot
is useful for:
- Creating a serializable, unproxied version of the state.
- Keeping a history of immutable states.
- Comparing states to see if they have changed.
- Resolving promises within the state for async operations.
To use snapshot
without React, import it from valtio/vanilla
:
import { proxy, snapshot } from 'valtio/vanilla';