or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

application-engine.mdcomponents.mdcontrollers.mddata-structures.mddebugging-development.mddestroyables-cleanup.mdindex.mdmodifiers.mdobject-model.mdreactivity-tracking.mdrouting.mdservices.mdtemplates-rendering.mdtesting.mdutilities.md
tile.json

reactivity-tracking.mddocs/

Reactivity & Tracking

Modern auto-tracking system that automatically updates UI when tracked properties change, eliminating manual observer management.

Capabilities

Tracked Decorator

Decorator for marking properties as tracked for automatic reactivity.

/**
 * Decorator for marking properties as tracked for automatic reactivity
 * @param target - Target object (class prototype)
 * @param key - Property key being decorated
 * @returns Property descriptor
 */
function tracked(target: any, key: string): PropertyDescriptor;

/**
 * Decorator factory for tracked properties with options
 * @param options - Tracking options
 * @returns Decorator function
 */
function tracked(options?: TrackedOptions): PropertyDecorator;

Usage Examples:

import { tracked } from "@glimmer/tracking";
import Component from "@glimmer/component";

export default class CounterComponent extends Component {
  @tracked count = 0;
  @tracked({ memoize: false }) timestamp = Date.now();
  
  get doubleCount() {
    return this.count * 2; // Automatically recomputes when count changes
  }
  
  increment() {
    this.count++; // Triggers UI update automatically
  }
  
  updateTimestamp() {
    this.timestamp = Date.now(); // Always triggers update due to memoize: false
  }
}

Cached Decorator

Decorator for memoized getters that automatically recompute when dependencies change.

/**
 * Decorator for memoized getters that automatically recompute when dependencies change
 * @param target - Target object (class prototype)  
 * @param key - Property key being decorated
 * @param descriptor - Property descriptor
 * @returns Modified property descriptor
 */
function cached(target: any, key: string, descriptor: PropertyDescriptor): PropertyDescriptor;

Usage Examples:

import { tracked, cached } from "@glimmer/tracking";

export default class ExpensiveComputationComponent extends Component {
  @tracked data = [];
  @tracked filter = '';
  
  @cached
  get filteredData() {
    // This expensive computation only runs when data or filter changes
    console.log('Computing filtered data...');
    return this.data.filter(item => 
      item.name.toLowerCase().includes(this.filter.toLowerCase())
    ).sort((a, b) => a.name.localeCompare(b.name));
  }
  
  @cached
  get statistics() {
    // Only recomputes when filteredData changes
    const data = this.filteredData;
    return {
      total: data.length,
      categories: [...new Set(data.map(item => item.category))].length,
      averagePrice: data.reduce((sum, item) => sum + item.price, 0) / data.length
    };
  }
}

Cache Primitives

Low-level cache creation and management functions for advanced tracking scenarios.

/**
 * Create a tracked cache that automatically invalidates when dependencies change
 * @param fn - Function to cache (should be pure)
 * @returns Cache object
 */
function createCache<T>(fn: () => T): Cache<T>;

/**
 * Get the current value from a cache, running the function if needed
 * @param cache - Cache object to get value from
 * @returns Cached value
 */
function getValue<T>(cache: Cache<T>): T;

/**
 * Check if a cache value is constant (will never change)
 * @param cache - Cache object to check
 * @returns Whether cache is constant
 */
function isConst<T>(cache: Cache<T>): boolean;

Usage Examples:

import { createCache, getValue } from "@glimmer/tracking";
import { tracked } from "@glimmer/tracking";

class DataProcessor {
  @tracked rawData = [];
  @tracked config = { sortBy: 'name', filterBy: '' };
  
  constructor() {
    // Create cache for expensive processing
    this.processedDataCache = createCache(() => {
      console.log('Processing data...');
      return this.rawData
        .filter(item => item.name.includes(this.config.filterBy))
        .sort((a, b) => {
          const field = this.config.sortBy;
          return a[field] > b[field] ? 1 : -1;
        });
    });
  }
  
  get processedData() {
    return getValue(this.processedDataCache);
  }
  
  updateData(newData) {
    this.rawData = newData; // Cache automatically invalidates
  }
  
  updateConfig(newConfig) {
    this.config = { ...this.config, ...newConfig }; // Cache invalidates
  }
}

Tracking Context

Functions for working with tracking contexts and validation.

/**
 * Track property access within a function for reactivity
 * @param fn - Function to track
 * @returns Result of function
 */
function track<T>(fn: () => T): T;

/**
 * Check if currently in a tracking context
 * @returns Whether in tracking context
 */
function isTracking(): boolean;

/**
 * Consume a tag to establish tracking relationship
 * @param tag - Tag to consume
 */
function consume(tag: Tag): void;

/**
 * Mark tracking as dirty to trigger updates
 * @param tag - Tag to mark as dirty
 */
function dirty(tag: Tag): void;

Validation System

Internal validation system for tracking dependencies and updates.

/**
 * Validate tracking state and check for updates
 * @param tag - Tag to validate
 * @returns Validation result
 */
function validate(tag: Tag): boolean;

/**
 * Create a new tracking tag
 * @returns New tag
 */
function createTag(): Tag;

/**
 * Combine multiple tags into a single tag
 * @param tags - Tags to combine
 * @returns Combined tag
 */
function combine(tags: Tag[]): Tag;

Property Change Batching

System for batching property changes to optimize rendering.

/**
 * Begin batching property changes
 */
function beginPropertyChanges(): void;

/**
 * End batching property changes and flush updates
 */
function endPropertyChanges(): void;

/**
 * Execute function with property change batching
 * @param fn - Function to execute with batching
 * @returns Result of function
 */
function withPropertyChanges<T>(fn: () => T): T;

Usage Examples:

import { beginPropertyChanges, endPropertyChanges } from "@ember/-internals/metal";

class BulkUpdater {
  @tracked items = [];
  
  updateMultipleItems(updates) {
    // Batch all changes to avoid multiple re-renders
    beginPropertyChanges();
    
    try {
      updates.forEach(update => {
        const item = this.items.find(i => i.id === update.id);
        if (item) {
          Object.assign(item, update);
        }
      });
    } finally {
      endPropertyChanges(); // Triggers single update
    }
  }
}

Advanced Tracking Patterns

Advanced patterns for complex tracking scenarios.

/**
 * Create untracked getter that doesn't participate in reactivity
 * @param target - Target object
 * @param key - Property key
 * @param descriptor - Property descriptor
 * @returns Untracked descriptor
 */
function untracked(target: any, key: string, descriptor: PropertyDescriptor): PropertyDescriptor;

/**
 * Execute function without tracking dependencies
 * @param fn - Function to execute
 * @returns Result of function
 */
function untrack<T>(fn: () T): T;

Types

interface Cache<T> {
  /** Internal cache implementation */
  [Symbol.toStringTag]: 'Cache';
}

interface Tag {
  /** Internal tag implementation */
  [Symbol.toStringTag]: 'Tag';
}

interface TrackedOptions {
  /** Whether to memoize the property value */
  memoize?: boolean;
}

interface TrackingContext {
  /** Tags being tracked in this context */
  tags: Tag[];
  
  /** Whether context is active */
  isTracking: boolean;
}

interface ValidationResult {
  /** Whether validation passed */
  isValid: boolean;
  
  /** Validation timestamp */
  revision: number;
}