Modern auto-tracking system that automatically updates UI when tracked properties change, eliminating manual observer management.
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
}
}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
};
}
}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
}
}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;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;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 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;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;
}