← Back to Home

🔓 Untrack - Advanced Reactivity Control

Read signals without creating dependencies. Perfect for optimization and preventing unnecessary updates!

📚 What You'll Learn

  • Use untrack() to read signals without tracking them
  • Prevent unnecessary re-computations and re-renders
  • When and why to use untrack for optimization
  • Advanced reactivity patterns for performance

🎮 Try It: See Untrack in Action

Compute both results, then change each signal to see the difference!

0
Count Signal
0
Dependency Signal
0
Tracked Result
(count + dependency)
0
Untracked Result
(count + untrack(dependency))

💡 Try this:

  1. Click "Compute Tracked" and "Compute Untracked"
  2. Increment the Count signal - both results update
  3. Increment the Dependency signal - only Tracked updates!
  4. The Untracked result doesn't re-compute because dependency was read inside untrack()

🚀 How It Works

❌ Without untrack() - Tracks Everything
const result = count + dependency;
// Re-computes when EITHER changes

Both signals are tracked as dependencies

✅ With untrack() - Selective Tracking
const result = count + untrack(() => dependency);
// Only re-computes when count changes!

Only count is tracked; dependency is ignored

💻 Complete Code Example

import { createSignal, createComputed, untrack } from 'signalforge/core';

const currentUser = createSignal({ id: 1, name: 'Alice' });
const theme = createSignal('dark');

// BAD: Re-computes when theme changes (unnecessary!)
const userDisplayBad = createComputed(() => {
  const user = currentUser.get();
  const themeColor = theme.get(); // Tracked!
  return `${user.name} (theme: ${themeColor})`;
});

// GOOD: Only re-computes when user changes
const userDisplayGood = createComputed(() => {
  const user = currentUser.get();
  const themeColor = untrack(() => theme.get()); // NOT tracked!
  return `${user.name} (theme: ${themeColor})`;
});

// Now changing theme won't trigger userDisplayGood!
theme.set('light'); // userDisplayBad updates ❌
                    // userDisplayGood stays same ✅

🌍 When To Use Untrack

🎨 Reading UI Preferences
Don't recompute data when theme/locale changes
const display = computed(() => {
  const data = mainData.get();
  const theme = untrack(() => uiTheme.get());
  return formatData(data, theme);
});
📊 Expensive Calculations
Skip tracking signals that change often but don't matter
const stats = computed(() => {
  const data = dataset.get();
  const precision = untrack(() => decimals.get());
  return calculateStats(data).toFixed(precision);
});
🔐 Auth Checks
Check permissions without tracking them
const canEdit = computed(() => {
  const post = currentPost.get();
  const userId = untrack(() => user.get().id);
  return post.authorId === userId;
});
⏰ Timestamps
Read current time without tracking it
const log = computed(() => {
  const message = logMessage.get();
  const t = untrack(() => currentTime.get());
  return `[${t}] ${message}`;
});

💡 Best Practices

1️⃣
Use untrack for performance optimization

When you need a value but don't want to recompute when it changes.

2️⃣
Don't overuse it

Only use when you have a specific performance reason. Most signals should be tracked!

3️⃣
Perfect for UI preferences

Theme, locale, display settings - things that don't affect data calculations.

4️⃣
Great for initial values

Read a signal once without tracking future changes.

⚡ Performance Impact

Using untrack() can significantly improve performance when you have:

  • Signals that change frequently (like mouse position, scroll position)
  • Expensive computations that you don't want to repeat unnecessarily
  • Many dependent computations that would all update

🎓 Next Steps

Master untrack? Try these advanced demos: