Skip to content

Watch Annotations

Explicitly mark properties for tracking (shallow comparison)

class Model {
@watch field = observable.box(false);
constructor() {
makeObservable(this);
}
}

Track with identity comparison only

class Model {
@watch.ref @observable items = [1, 2, 3];
constructor() {
makeObservable(this);
}
}
const model = new Model();
const watcher = Watcher.get(model);
runInAction(() => {
model.items.push(4); // No change detected (same array reference)
});
watcher.changed // false

Exclude properties from tracking

class Model {
@unwatch @observable internalState = false;
@observable userField = false;
@unwatch
@computed
get derivedState() {
return this.internalState ? "active" : "inactive";
}
constructor() {
makeObservable(this);
}
}
const model = new Model();
const watcher = Watcher.get(model);
runInAction(() => {
model.internalState = true; // Not tracked
});
watcher.changed // false
watcher.changedKeys // Set() - derivedState is also not tracked

Use @nested to track changes in nested object properties. Read Nested section for detail.

  • Each nested object gets its own Watcher instance automatically
  • Nested watchers are independent - calling reset() on a child watcher doesn't affect the parent
class Parent {
@nested @observable child = new Child();
@nested @observable items = [new Item()];
constructor() {
makeObservable(this);
}
}
class Child {
@observable value = false;
constructor() {
makeObservable(this);
}
}
const parent = new Parent();
const watcher = Watcher.get(parent);
runInAction(() => {
parent.child.value = true;
parent.items[0].value = true;
});
watcher.changed // true
watcher.changedKeys // Set([]) - no direct property changes
watcher.changedKeyPaths // Set(["child.value", "items.0.value"]) - nested change tracked
// Nested watchers are independent
const childWatcher = Watcher.get(parent.child);
childWatcher.reset(); // Does NOT affect parent watcher
watcher.changed // still true