Lazy Initialization
Problem
State initialization runs expensive calculations on every component mount, even when recreating the same values. Reading from localStorage, parsing JSON, or filtering large arrays happens repeatedly during development with hot reloads. Component instantiation becomes a performance bottleneck as initialization logic grows.
Solution
Defer expensive operations until their results are actually needed rather than computing them upfront. This improves initial load time by spreading work across the user’s session.
Example
This example shows how to defer parsing localStorage data until it’s actually needed using a getter, rather than doing it immediately in the constructor.
// Framework-agnostic lazy initialization
class Component extends HTMLElement {
constructor() {
super();
// Don't initialize expensive state in constructor
// Keep constructor fast and lightweight
}
get data() {
// Lazy initialization - only runs once when first accessed
if (!this._data) {
// Parse localStorage only when someone accesses this.data
this._data = JSON.parse(localStorage.getItem('data'));
}
return this._data;
}
connectedCallback() {
// Data is only parsed when needed (when this.data is accessed)
this.innerHTML = `<div>${this.data}</div>`;
}
}
Benefits
- Improves performance by running expensive calculations only once on first render.
- Reduces wasted work during hot reloads in development.
- Speeds up component instantiation by deferring initialization.
- Particularly valuable for reading from localStorage or parsing JSON.
Tradeoffs
- Adds syntax complexity with function initializers instead of direct values.
- Can be misused, hiding expensive operations that should be optimized differently.
- May delay the performance cost to first render instead of eliminating it.
- Not needed for simple, inexpensive default values.