observe
Runs a function now and again whenever a reactive value it read changes.
observe(fn: () => unknown): () => voidobserve(() => {
localStorage.setItem("draft", todo.draft);
});
observe(() => {
const id = setInterval(refresh, props.interval);
return () => clearInterval(id);
});Parameters
| Parameter | Type | Meaning |
|---|---|---|
fn |
() => unknown |
Runs immediately, then after any value it read changes. If it returns a function, that function runs before the next run and when the observer stops |
Returns a stop() function.
Behaviour
| Topic | Behaviour |
|---|---|
| Dependencies | Collected fresh on every run: only what the last run read is watched |
| Batching | Re-runs are queued and run once per microtask, after all synchronous writes |
| Lifetime | In a component script, stops automatically when the element is removed. The exported observe from @realmorg/realm is not tied to any element |
refs |
An observer at the top of a script sees refs once the markup exists, because refs is reactive |
| Errors | A throwing observer is reported with console.error. Other observers keep running |
| Loops | If observers keep re-triggering each other for 100 rounds in one flush, the queue is cleared and Update loop detected is reported |
| Reading without subscribing | Wrap reads in untrack, imported from @realmorg/realm (it is not one of the script helpers) |
Example
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>observe demo</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@realmorg/ui@alpha/dist/theme.css">
<script type="module" src="https://cdn.jsdelivr.net/npm/@realmorg/realm@alpha/dist/realm.js"></script>
</head>
<body>
<import-element from="./title-mirror.html"></import-element>
<title-mirror></title-mirror>
</body>
</html><custom-element name="title-mirror">
<script>
const draft = useState({ text: "REALM" });
const log = useState({ runs: 0 });
let runs = 0;
observe(() => {
refs.preview?.style.setProperty("letter-spacing", `${draft.text.length / 20}em`);
log.runs = ++runs;
});
</script>
<style>
:host {
display: grid;
gap: calc(var(--spacing) * 2);
width: min(calc(var(--spacing) * 100), 100%);
}
</style>
<input aria-label="Text" _value="#draft.text" oninput="draft.text = this.value">
<strong ref="preview"><slot name="#draft.text"></slot></strong>
<small>Observer runs: <slot name="#log.runs"></slot></small>
</custom-element>Related
Calling other components from an observer
An observer subscribes to everything it reads, including state read inside another component's methods. Wrap such calls in untrack so the observer runs only when its own inputs change:
observe(() => {
if (order.status === "error") untrack(() => refs.dialog.showModal());
});