Derived State & Effects
Use derived observables when you need another value. Use effects when you need to do work because a value changed.
Derived Values
Section titled “Derived Values”Use useObservable(() => ...) for values that can be computed from other observables:
import { useObservable } from "@legendapp/state/react";
function CartBadge() { const cart$ = useObservable<Record<string, number>>({}); const cartCount$ = useObservable(() => Object.values(cart$.get()).reduce((total, quantity) => total + quantity, 0) );
return <span>{cartCount$.get()} items</span>;}This keeps derived state declarative and removes extra synchronization code.
Effects
Section titled “Effects”Three hook flavors cover the common effect shapes:
useObserve(fn)— runs immediately and re-runs whenever any observable read inside it changes.useWatch(selector, effect, opts?)— reacts to a specific source. Skips the initial value by default (immediate: trueto include it).useWhenever(selector, effect, opts?)— fires when the selector becomes truthy ({ once: true }to self-dispose after the first fire).
Watch Example
Section titled “Watch Example”import { useObservable } from "@legendapp/state/react";import { useWatch } from "@usels/core";
function SearchSync() { const query$ = useObservable("");
useWatch(query$, (query) => { console.log("query changed:", query); });
return <input value={query$.get()} onChange={(event) => query$.set(event.currentTarget.value)} />;}Avoid Mirroring Derived State With Effects
Section titled “Avoid Mirroring Derived State With Effects”If a value can be derived, derive it:
const fullName$ = useObservable(() => `${firstName$.get()} ${lastName$.get()}`);Use an effect for external work such as DOM sync, analytics, storage, network requests, or calls into another store.
Avoid Early Return With .get()
Section titled “Avoid Early Return With .get()”Observable reads outside JSX or outside a reactive context are plain snapshots. An early return based on .get() does not re-run the component when the value changes:
// ❌ Early return — the component won't re-render when error$ changesif (error$.get()) return <p>Error</p>;if (!isLoaded$.get()) return <p>Loading...</p>;return <div>{data$.name.get()}</div>;Instead, keep everything inside a single return and wrap the ternary in a JSX fragment (<>...</>). The Babel plugin only tracks .get() calls that appear inside JSX — a bare return ternary is not JSX, so wrapping it in a fragment puts it inside the plugin’s detection scope:
// ✅ Ternary inside JSX — each .get() is a fine-grained leafreturn <> {error$.get() ? <p>Error: {error$.get()?.message}</p> : !isLoaded$.get() ? <p>Loading...</p> : <div>{data$.name.get()}</div>}</>;Or derive a view-state observable that captures the branching logic:
const view$ = useObservable(() => { if (error$.get()) return "error" as const; if (!isLoaded$.get()) return "loading" as const; return "ready" as const;});
return <> {view$.get() === "error" ? <p>Error</p> : view$.get() === "loading" ? <p>Loading...</p> : <div>{data$.name.get()}</div>}</>;Scope Context Equivalents
Section titled “Scope Context Equivalents”Inside "use scope", use the non-hook versions of these APIs. They are auto-cleaned up when the scope unmounts.
| Hook | Scope equivalent |
|---|---|
useObservable(() => ...) | observable(() => ...) |
useObserve(fn) | observe(fn) |
useWatch(source, fn) | watch(source, fn) |
useWhenever(source, fn) | whenever(source, fn) |
import { observable, observe, watch, whenever } from "@usels/core";
function SearchSync() { "use scope"; const query$ = observable("");
// Derived value const queryLength$ = observable(() => query$.get().length);
// Side-effect observe(() => { console.log("query:", query$.get()); });
// Watch with old/new watch(query$, (query, prev) => { console.log("changed from", prev, "to", query); });
return <input value={query$.get()} onChange={(e) => query$.set(e.currentTarget.value)} />;}For the full effects reference, see Effects API.
Related
Section titled “Related”- Observable-First Mental Model — why derived observables beat mirrored state.
- Data Fetching — effects that sync with remote sources.
- Effects API — canonical reference for
observe(),watch(),whenever()in scope context.