untrack
Runs a function that reads signals without tracking them as dependencies of the active observer.
Se autoimporta en las aplicaciones del framework.
import { untrack } from "@meekojs/meeko";Inside an effect, a computed or the template, reading signal.value subscribes the observer: it runs again when the signal changes. untrack(fn) runs fn with no active observer, so the signals read inside it are not tracked. Use it to read a contextual value (the user, the current page, an option) without changes to that value triggering the work.
It is exported from @meekojs/meeko and auto-imported in framework applications.
Usage#
import { effect, signal, untrack } from "@meekojs/meeko";
const query = signal("");
const page = signal(1);
// Reruns when query changes; page is read without subscribing.
effect(() => {
console.log(`Search "${query.value}" from page ${untrack(() => page.value)}`);
});
page.value = 2; // no log
query.value = "a"; // log: Search "a" from page 2Type#
function untrack<T>(fn: () => T): TParameters#
| Parameter | Type | Default | Description |
|---|---|---|---|
fn |
() => T |
required | Function that reads signals. It runs immediately, once. |
Return value#
The return value of fn. Outside an observer, untrack(fn) is equivalent to calling fn().
Examples#
Logging an event with data that should not repeat it#
import { effect, signal, untrack } from "@meekojs/meeko";
const route = signal("/");
const userId = signal("anon");
effect(() => {
const path = route.value; // the dependency
void fetch("/api/analytics", {
method: "POST",
body: JSON.stringify({ path, user: untrack(() => userId.value) }),
});
});Changing users does not send a new visit; changing routes does.
An initial value that should not follow the prop#
In a computed, untrack determines what should not cause recalculation:
<!-- app/components/Draft.meeko -->
<script lang="ts">
const props = defineProps<{ text: string; locale: string }>();
const draft = signal(untrack(() => props.text)); // initial copy, by design
const words = computed(() => draft.value.trim().split(/\s+/).filter(Boolean).length);
</script>
<template>
<textarea model="draft"></textarea>
<p>{words.value} words</p>
</template>props.text is read only once; the user edits the copy in draft.
Calling external code from an effect#
A library function can read signals you do not control. untrack prevents the effect from depending on them:
import { effect, signal, untrack } from "@meekojs/meeko";
declare function notify(message: string): void; // may read internal signals
const saved = signal(0);
effect(() => {
if (saved.value > 0) untrack(() => notify(`Saved ${saved.value}`));
});Common mistakes#
Returning the signal instead of its value#
untrack only prevents subscription while fn runs. If it returns the signal, the read happens later, outside untrack, and is tracked.
effect(() => {
const current = untrack(() => page);
console.log(query.value, current.value); // page vuelve a ser dependencia
});There is no error: the effect runs again when page changes. Read .value inside the function:
effect(() => {
console.log(query.value, untrack(() => page.value));
});Using it to hide a loop#
If an effect writes to a signal it also reads, untrack around the read prevents the error Reactive update cycle exceeded 1000 iterations, but leaves manually derived state.
effect(() => {
total.value = untrack(() => total.value) + price.value;
});total accumulates every change to price and never goes back. If the value depends on others, use a computed:
const total = computed(() => items.value.reduce((sum, item) => sum + item.price, 0));Notes#
- Server and browser. It works the same in both.
- Writes.
untrackdoes not change writes: an assignment insidefnnotifies observers as usual. To group them, usebatch. - Meeko callbacks.
onMountedand functions Meeko runs after hydration already run without tracking: you do not needuntrackinside them. - Template. In a template expression,
untrackprevents that binding from updating when the signal changes. You rarely need it; it usually indicates that the data should be a constant.