Computed Properties
Valtio supports computed properties using native JavaScript getters and setters on your proxy object. This allows you to create derived state that automatically updates when its dependencies change.
Note: Getters are an advanced JavaScript feature. While they work as expected, be mindful of the behavior of
thisinside them.
Using a Simple Getter
You can define a getter on your proxy object to compute a value based on other properties.
const state = proxy({
count: 1,
get doubled() {
return this.count * 2;
},
});
console.log(state.doubled); // 2
state.count = 10;
console.log(state.doubled); // 20
When used with useSnapshot in React, components that access snap.doubled will automatically re-render when state.count changes.
Using Getters and Setters
You can also define a setter to allow a computed property to be "settable," which in turn updates the source state.
const state = proxy({
count: 1,
get doubled() {
return state.count * 2;
},
set doubled(newValue) {
state.count = newValue / 2;
},
});
state.doubled = 4;
console.log(state.count); // 2
Caching with proxy-memoize
By default, getters on the state proxy are re-calculated on every access. If your computed property is expensive, you can memoize it using Valtio's sister library, proxy-memoize.
This library uses the same usage-tracking mechanism as useSnapshot, so the getter will only be re-calculated if the specific properties it depends on have changed.
import { proxy, snapshot } from 'valtio';
import { memoize } from 'proxy-memoize';
const memoizedDoubled = memoize((snap) => snap.count * 2);
const state = proxy({
count: 1,
text: 'hello',
get doubled() {
// Pass the snapshot to the memoized function
return memoizedDoubled(snapshot(state));
},
});
In this setup, changing state.text will not cause memoizedDoubled to re-run.
Rules and Gotchas
thisContext: Inside a getter,thisrefers to the object the getter was called on. If you callstate.doubled,thisis thestateproxy. If you callsnap.doubled,thisis thesnapobject.-
Sibling Properties Only: A computed property should generally only reference sibling properties (properties on the same object level). Accessing parent or other external state can lead to unexpected behavior.
// ✅ GOOD: `this` refers to the current object const user = proxy({ name: 'John', get greeting() { return 'Hello ' + this.name; }, }); // ❌ BAD: `this` points to `greetings`, not `state` const state = proxy({ user: { name: 'John' }, greetings: { get greetingEn() { return 'Hello ' + this.user.name; // `this.user` is undefined }, }, }); -
Alternatives for Cross-State Derivation: If you need to derive state from multiple different proxies, consider using
watchfromvaltio/utilsorderivefrom thederive-valtiopackage.