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';