Skip to content

Best Practices โ€‹

Follow these best practices to create robust, performant, and maintainable KIMU extensions.

๐Ÿ—๏ธ Architecture and Organization โ€‹

1. Consistent File Structure โ€‹

extensions/
  my-extension/
    component.ts          # Main logic
    view.html            # UI template  
    style.css            # Styles
    types.ts             # TypeScript types (optional)
    utils.ts             # Utility functions (optional)
    README.md            # Documentation (optional)

2. Naming Convention โ€‹

typescript
// โœ… GOOD: Descriptive and consistent names
@KimuComponent({
  tag: 'user-profile-card',        // kebab-case, descriptive
  name: 'User Profile Card',       // Spaces, Title Case
  path: 'user-profile-card'        // Same as tag
})
export class UserProfileCard extends KimuComponentElement {
  // Private properties with underscore or private keyword
  private _userData: UserData | null = null;
  private isLoading = false;
  
  // Public methods camelCase
  public getUserData(): UserData | null {
    return this._userData;
  }
  
  // Private methods with prefix
  private handleUserUpdate(): void {
    // ...
  }
}

๐ŸŽฏ State Management โ€‹

1. Immutable State โ€‹

typescript
// โœ… GOOD: Immutable state with helper methods
export class StateManagementExample extends KimuComponentElement {
  private state = {
    counter: 0,
    items: [] as Item[],
    loading: false,
    error: null as string | null
  };

  // Centralized method for state updates
  private updateState(updates: Partial<typeof this.state>) {
    this.state = { ...this.state, ...updates };
    this.refresh();
  }

  // Specific methods for actions
  private incrementCounter() {
    this.updateState({ counter: this.state.counter + 1 });
  }

  private addItem(item: Item) {
    this.updateState({ 
      items: [...this.state.items, item] 
    });
  }

  getData() {
    return {
      ...this.state,
      // Action handlers
      onIncrement: () => this.incrementCounter(),
      onAddItem: (item: Item) => this.addItem(item)
    };
  }
}

โšก Performance and Optimization โ€‹

1. Avoid Unnecessary Re-renders โ€‹

typescript
// โœ… GOOD: Check changes before refresh
export class OptimizedComponent extends KimuComponentElement {
  private lastRenderedData: any = null;

  getData() {
    const currentData = {
      items: this.items,
      counter: this.counter,
      // ...
    };

    // Only refresh if data actually changed
    if (!this.dataEquals(currentData, this.lastRenderedData)) {
      this.lastRenderedData = { ...currentData };
    }

    return currentData;
  }

  private dataEquals(a: any, b: any): boolean {
    return JSON.stringify(a) === JSON.stringify(b);
  }
}

2. Debouncing and Throttling โ€‹

typescript
// โœ… GOOD: Debouncing for heavy inputs
export class DebouncedSearchComponent extends KimuComponentElement {
  private searchQuery = '';
  private searchResults: any[] = [];
  private searchTimeout?: number;

  getData() {
    return {
      searchQuery: this.searchQuery,
      searchResults: this.searchResults,
      
      onSearchInput: (event: Event) => {
        const input = event.target as HTMLInputElement;
        this.searchQuery = input.value;
        
        // Debounce search API calls
        if (this.searchTimeout) {
          clearTimeout(this.searchTimeout);
        }
        
        this.searchTimeout = window.setTimeout(() => {
          this.performSearch();
        }, 300);
        
        this.refresh(); // Update UI immediately for input
      }
    };
  }

  private async performSearch() {
    if (this.searchQuery.length < 2) {
      this.searchResults = [];
      this.refresh();
      return;
    }

    try {
      const results = await this.searchAPI(this.searchQuery);
      this.searchResults = results;
      this.refresh();
    } catch (error) {
      console.error('Search error:', error);
    }
  }

  onDestroy() {
    if (this.searchTimeout) {
      clearTimeout(this.searchTimeout);
    }
  }
}

๐Ÿ›ก๏ธ Error Handling and Resilience โ€‹

1. Error Boundaries for Components โ€‹

typescript
// โœ… GOOD: Robust error handling
export class ResilientComponent extends KimuComponentElement {
  private error: Error | null = null;
  private retryCount = 0;
  private maxRetries = 3;

  getData() {
    // If there's an error, show fallback UI
    if (this.error) {
      return this.getErrorState();
    }

    try {
      return this.getNormalState();
    } catch (error) {
      this.handleError(error);
      return this.getErrorState();
    }
  }

  private getNormalState() {
    return {
      data: this.processData(),
      loading: this.isLoading,
      onAction: () => this.safePerformAction()
    };
  }

  private getErrorState() {
    return {
      error: this.error?.message || 'Unknown error',
      canRetry: this.retryCount < this.maxRetries,
      onRetry: () => this.retry(),
      onReset: () => this.reset()
    };
  }

  private handleError(error: any) {
    console.error('Component error:', error);
    this.error = error instanceof Error ? error : new Error(String(error));
  }

  private async safePerformAction() {
    try {
      await this.performAction();
    } catch (error) {
      this.handleError(error);
      this.refresh();
    }
  }

  private retry() {
    if (this.retryCount < this.maxRetries) {
      this.retryCount++;
      this.error = null;
      this.refresh();
    }
  }
}

๐Ÿ“ฆ Memory Management โ€‹

1. Resource Cleanup Pattern โ€‹

typescript
// โœ… GOOD: Systematic memory management
export class MemoryEfficientComponent extends KimuComponentElement {
  private resources = new ComponentResourceManager();
  private cache = new Map<string, any>();
  private maxCacheSize = 100;

  onInit() {
    // Register all resources for automatic cleanup
    const interval = window.setInterval(() => this.updateData(), 1000);
    this.resources.addTimer(interval);

    const observer = new IntersectionObserver(this.handleIntersection);
    this.resources.addObserver(observer);

    this.resources.addEventListener(document, 'visibilitychange', this.handleVisibilityChange);
  }

  onDestroy() {
    // Automatic cleanup of all resources
    this.resources.cleanup();
    this.clearCache();
  }

  private addToCache(key: string, value: any) {
    // Cache management with memory limit
    if (this.cache.size >= this.maxCacheSize) {
      const firstKey = this.cache.keys().next().value;
      this.cache.delete(firstKey);
    }
    this.cache.set(key, value);
  }

  private clearCache() {
    this.cache.clear();
  }
}

class ComponentResourceManager {
  private timers: number[] = [];
  private listeners: Array<{target: EventTarget, event: string, handler: EventListener}> = [];
  private observers: (IntersectionObserver | MutationObserver)[] = [];

  addTimer(id: number) {
    this.timers.push(id);
  }

  addEventListener(target: EventTarget, event: string, handler: EventListener) {
    this.listeners.push({ target, event, handler });
    target.addEventListener(event, handler);
  }

  addObserver(observer: IntersectionObserver | MutationObserver) {
    this.observers.push(observer);
  }

  cleanup() {
    // Systematic cleanup of all resources
    this.timers.forEach(id => {
      clearInterval(id);
      clearTimeout(id);
    });

    this.listeners.forEach(({ target, event, handler }) => {
      target.removeEventListener(event, handler);
    });

    this.observers.forEach(observer => observer.disconnect());

    // Reset
    this.timers = [];
    this.listeners = [];
    this.observers = [];
  }
}

๐ŸŽฏ Best Practices Summary โ€‹

โœ… Always Do โ€‹

  • Use TypeScript for type safety
  • Implement cleanup in onDestroy()
  • Handle errors gracefully
  • Use immutable state
  • Implement logging for debugging
  • Validate external data
  • Use consistent naming conventions

โŒ Avoid โ€‹

  • Memory leaks (timers, listeners)
  • Direct state mutations
  • Heavy synchronous operations in onInit()
  • DOM access in onInit()
  • Calling refresh() in infinite loops
  • Unhandled errors
  • Untyped code

๐Ÿš€ For Performance โ€‹

  • Debounce expensive operations
  • Cache computational results
  • Lazy load heavy dependencies
  • Check changes before re-rendering
  • Use weak references where appropriate

KIMU extensions following these best practices will be robust, performant, and maintainable, ensuring excellent user experience and long-term development ease.

Released under Creative Commons Attribution 4.0 International (CC BY 4.0)