untracked
Read signals without creating reactive dependencies.
API
function untracked<T>(fn: () => T): T
fn
: A function to execute without tracking signal dependencies.- Returns: The value returned by the function
fn
.
TypeScript
The untracked
function is generic and preserves the return type of the function it wraps.
type UntrackedFunction<T> = () => T;
const value: number = untracked(() => {
// read some signals...
return 42;
});
Basic Usage
Use untracked
to access a signal’s value inside a reactive scope (like computed
or effect
) without making that scope depend on the signal.
import { signal, computed, untracked } from '@hellajs/core';
const value = signal(5);
const multiplier = signal(10);
const result = computed(() => {
// `result` will update when `value` changes...
const currentValue = value();
// ...but it will NOT update when `multiplier` changes.
const currentMultiplier = untracked(() => multiplier());
return currentValue * currentMultiplier;
});
console.log(result()); // 50
value(6);
console.log(result()); // 60 (recomputed because `value` is a dependency)
multiplier(20);
console.log(result()); // 60 (NOT recomputed because `multiplier` is untracked)
Key Concepts
Dependency Isolation
Creates a reactive boundary where signals can be read without establishing dependency relationships with the calling context.
const tracked = signal('tracked');
const untracked = signal('untracked');
const result = computed(() => {
return tracked() + untracked(() => untracked());
}); // Only depends on 'tracked', not 'untracked'
Selective Tracking
Enables fine-grained control over which signals trigger reactive updates, preventing unnecessary recalculations.
const data = signal('data');
const debugMode = signal(false);
effect(() => {
console.log(data()); // This creates a dependency
if (untracked(() => debugMode())) { // This doesn't
console.log('Debug info');
}
});
Debug-Safe Reading
Essential for debug logging and conditional features that shouldn’t influence reactive behavior.
const computation = computed(() => {
const result = expensiveCalculation();
if (untracked(() => debugEnabled())) {
console.log('Result:', result); // Debug doesn't affect reactivity
}
return result;
});
Performance Optimization
Prevents unwanted dependencies that could cause expensive computations to run more frequently than necessary.
const counter = signal(0);
const metadata = signal({ version: 1 });
const optimized = computed(() => {
const count = counter();
const version = untracked(() => metadata().version); // No dependency on metadata
return { count, version };
});
Important Considerations
Overusing untracked
Most of the time, you want dependencies to be tracked automatically. Only use untracked
when you have a specific reason to prevent a dependency.
// ❌ Breaking necessary reactivity
const result = computed(() => untracked(() => signal()));
// ✅ Only untrack what shouldn't be reactive
const result = computed(() => signal() + untracked(() => debugValue()));
Debug Code Dependencies
Always wrap debug-only signal reads in untracked
to avoid unwanted dependencies.
// ❌ debugMode creates unwanted dependency
const c = computed(() => {
if (debugMode()) console.log('calculating...');
return expensiveCalculation();
});
// ✅ Debug code doesn't affect reactivity
const c = computed(() => {
if (untracked(() => debugMode())) console.log('calculating...');
return expensiveCalculation();
});