Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-06-01 - Icon Data Lookup Caching
**Learning:** The IconifyIcon component dynamically parses and resolves icon data (sometimes iterating over 11 icon sets for fallback lookups) on every render, which is expensive given the number of icons typically rendered in a dashboard.
**Action:** Always cache the results of expensive, static configuration lookups (like icon resolving) outside the render cycle or add memoization to prevent recalculating on every re-render.
18 changes: 16 additions & 2 deletions client/src/components/base/IconifyIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,31 @@ const iconSets: Record<string, IconifyJSON> = {
"mdi-light": mdiLightIcons,
};

// Cache to store resolved icon data and prevent redundant lookups
// This avoids O(N) iteration over multiple icon sets for prefix-less icons on every render
const iconDataCache = new Map<string, any>();

const iconData = (icon: string) => {
if (iconDataCache.has(icon)) {
return iconDataCache.get(icon);
}

const [prefix, name] = icon.includes(":") ? icon.split(":") : ["", icon];

if (prefix && iconSets[prefix]) {
const data = getIconData(iconSets[prefix], name);
if (data) return data;
if (data) {
iconDataCache.set(icon, data);
return data;
}
}

for (const [_, icons] of Object.entries(iconSets)) {
const data = getIconData(icons, name);
if (data) return data;
if (data) {
iconDataCache.set(icon, data);
return data;
}
}
};

Expand Down