Skip to content

Extension Lifecycle โ€‹

The lifecycle of KIMU extensions follows a predictable pattern that allows you to properly manage initialization, rendering, and cleanup.

๐Ÿ”„ Lifecycle Hooks โ€‹

1. onInit() - Initialization โ€‹

2. onRender() - Post-Rendering โ€‹

3. onDestroy() - Cleanup โ€‹


๐Ÿš€ 1. onInit() - Initial Setup โ€‹

Executed ONCE when the extension is loaded for the first time.

typescript
@KimuComponent({
  tag: 'lifecycle-demo',
  name: 'Lifecycle Demo'
})
export class LifecycleDemo extends KimuComponentElement {
  private intervalId?: number;
  private eventListener?: (e: Event) => void;
  private data: any[] = [];

  onInit(): void {
    console.log('๐Ÿš€ 1. onInit - Initialization');
    
    // โœ… Initial setup
    this.setupEventListeners();
    this.loadInitialData();
    this.startPeriodicUpdates();
  }

  private setupEventListeners() {
    this.eventListener = (e: Event) => {
      console.log('Event received:', e);
      this.refresh();
    };
    
    document.addEventListener('kimu:data-update', this.eventListener);
  }

  private async loadInitialData() {
    try {
      const response = await fetch('/api/initial-data');
      this.data = await response.json();
      this.refresh(); // Update UI with new data
    } catch (error) {
      console.error('Data loading error:', error);
    }
  }

  private startPeriodicUpdates() {
    this.intervalId = window.setInterval(() => {
      this.updateTimestamp();
    }, 1000);
  }

  getData() {
    return {
      data: this.data,
      timestamp: new Date().toLocaleTimeString(),
      status: 'Active'
    };
  }
}

What to Do in onInit() โ€‹

โœ… DoโŒ Don't
Setup event listenersDirect DOM manipulation
Load initial dataOperations depending on DOM
Initialize timersHeavy synchronous operations
Configure stateAccess non-rendered elements

๐ŸŽจ 2. onRender() - Post-Rendering โ€‹

Executed EVERY TIME after the template has been rendered.

typescript
export class RenderingExample extends KimuComponentElement {
  private chartInstance?: any;

  onRender(): void {
    console.log('๐ŸŽจ 2. onRender - Post-rendering');
    
    // โœ… Safe DOM manipulation
    this.attachDOMHandlers();
    this.initializeChart();
    this.updateDOMClasses();
  }

  private attachDOMHandlers() {
    // Safe access to DOM elements
    const button = this.querySelector('.special-button');
    if (button) {
      button.addEventListener('click', this.handleSpecialClick);
    }
  }

  private initializeChart() {
    const chartContainer = this.querySelector('.chart-container');
    if (chartContainer && !this.chartInstance) {
      // Initialize chart library only if container exists
      this.chartInstance = new Chart(chartContainer, {
        // Chart configuration
      });
    }
  }

  private handleSpecialClick = (e: Event) => {
    console.log('Special click:', e);
  }

  getData() {
    return {
      isActive: true,
      isLoading: false,
      chartData: [1, 2, 3, 4, 5]
    };
  }
}

What to Do in onRender() โ€‹

โœ… DoโŒ Don't
DOM manipulationSetup global event listeners
Initialize UI librariesHeavy async operations
Update CSS classesAPI calls
Bind specific eventsState changes causing infinite re-render

๐Ÿงน 3. onDestroy() - Cleanup โ€‹

Executed when the extension is removed from the layout.

typescript
export class CleanupExample extends KimuComponentElement {
  private intervalId?: number;
  private eventListener?: (e: Event) => void;
  private observers: IntersectionObserver[] = [];
  private abortController = new AbortController();

  onInit(): void {
    // Setup with tracking for cleanup
    this.setupPeriodicUpdates();
    this.setupEventListeners();
    this.setupObservers();
  }

  onDestroy(): void {
    console.log('๐Ÿงน 3. onDestroy - Cleanup');
    
    // โœ… Complete cleanup of all resources
    this.clearTimers();
    this.removeEventListeners();
    this.disconnectObservers();
    this.cancelAsyncOperations();
  }

  private clearTimers() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = undefined;
    }
  }

  private removeEventListeners() {
    if (this.eventListener) {
      document.removeEventListener('resize', this.eventListener);
      this.eventListener = undefined;
    }
  }

  private disconnectObservers() {
    this.observers.forEach(observer => observer.disconnect());
    this.observers = [];
  }

  private cancelAsyncOperations() {
    this.abortController.abort();
  }

  getData() {
    return {
      status: 'Active',
      activeTimers: this.intervalId ? 1 : 0,
      activeObservers: this.observers.length
    };
  }
}

What to Do in onDestroy() โ€‹

โœ… Always Doโš ๏ธ Attention
Clear timers/intervalsDon't call refresh()
Remove event listenersDon't start new async operations
Disconnect observersDon't access DOM
Cancel async operationsNo complex logic
Reset referencesOperations must be synchronous

๐Ÿšจ Common Mistakes to Avoid โ€‹

โŒ Don't Do - Memory Leaks โ€‹

typescript
// โŒ WRONG: Timer not cleaned up
onInit() {
  setInterval(() => {
    this.refresh();
  }, 1000); // Memory leak!
}

// โŒ WRONG: Listener not removed
onInit() {
  document.addEventListener('click', this.handleClick); // Memory leak!
}

โœ… Do - Proper Management โ€‹

typescript
// โœ… CORRECT: Timer with cleanup
private timerId?: number;

onInit() {
  this.timerId = window.setInterval(() => {
    this.refresh();
  }, 1000);
}

onDestroy() {
  if (this.timerId) {
    clearInterval(this.timerId);
  }
}

๐Ÿ“š Next Steps โ€‹

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