Skip to content
Observe

useWatch

Runs a reactive effect that skips the first effect execution by default (lazy mode). Pass immediate: true to execute the effect immediately on setup (eager mode). The selector always tracks dependencies; the effect is suppressed on the initial call unless immediate: true.

Selector supports three forms:

  • Single observableuseWatch(count$, (v) => ...)
  • Array of observablesuseWatch([a$, b$], ([a, b]) => ...), like watch([ref1, ref2], ...)
  • Reactive functionuseWatch(() => count$.get(), (v) => ...)
import {
import useObservable
useObservable
,
import useWatch
useWatch
} from "@usels/web";
function
function Component(): void
Component
() {
const
const query$: any
query$
=
import useObservable
useObservable
("");
// ✅ Lazy (default) — runs only when query$ changes
import useWatch
useWatch
(
const query$: any
query$
, (
value: any
value
) => {
any
console
.
any
log
("search:",
value: any
value
);
});
}

Pass immediate: true to execute the effect immediately on setup, in addition to triggering on source changes.

import { useObservable, useWatch } from "@usels/web";
function Component() {
const query$ = useObservable("");
// ✅ Also executes the effect immediately with the initial value
useWatch(
query$,
(value) => {
console.log("value:", value);
},
{ immediate: true }
);
}

Watch multiple sources at once. The effect receives a tuple of current values.

import { useObservable, useWatch } from "@usels/web";
function Component() {
const query$ = useObservable("");
const page$ = useObservable(1);
useWatch([query$, page$], ([query, page]) => {
console.log("fetch page", page, "for query:", query);
});
}

Use a function to compute a derived value. The function runs on every dep change to maintain tracking; the effect receives the computed result.

import { useObservable, useWatch } from "@usels/web";
function Component() {
const firstName$ = useObservable("John");
const lastName$ = useObservable("Doe");
useWatch(
() => `${firstName$.get()} ${lastName$.get()}`,
(fullName) => {
console.log("name changed:", fullName);
}
);
}

The schedule option controls when the effect runs relative to Legend-State’s batch cycle.

  • schedule: 'sync' — runs synchronously inside the batch (equivalent to Legend-State immediate: true)
  • schedule: 'deferred' — runs after the batch ends (equivalent to Legend-State immediate: false)
  • omitted — uses Legend-State’s default batching
useWatch(count$, (v) => console.log(v), { schedule: "sync" });
export { watch, type WatchOptions, type WatchSource, type Effector } from "./core";
export type UseWatch = typeof watch;
/**
* Runs a reactive effect that skips the initial run on mount by default.
* Pass `immediate: true` to also fire on mount (eager mode).
*
* Selector supports three forms:
* - Single `Observable<T>`
* - Array `[Observable<A>, Observable<B>]` — effect receives `[A, B]`
* - Reactive function `() => T`
*
* @param selector - Observable, array of Observables, or reactive read function.
* @param effect - Side-effect callback.
* @param options - `immediate` (fire on mount), `schedule` (batch timing).
*
* @example
* ```tsx twoslash
* // @noErrors
* import { useWatch } from "@usels/core";
* import { observable } from "@legendapp/state";
*
* const query$ = observable("");
* const page$ = observable(1);
*
* // Lazy (default) — skips mount
* useWatch(query$, (value) => { console.log("changed:", value); });
*
* // Eager — also fires on mount
* useWatch(query$, (value) => { console.log("value:", value); }, { immediate: true });
*
* // Array of observables
* useWatch([query$, page$] as const, ([query, page]) => {
* console.log("fetch:", query, page);
* });
* ```
*/
export declare const useWatch: UseWatch;

View on GitHub