diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..a38096b --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-24 - [Global Caching for IconifyIcon Lookups] +**Learning:** Prefix-less icon lookups in `IconifyIcon.tsx` iterate over multiple large icon sets on every render. Because the component is used heavily (192 times in the project), this linear lookup becomes a rendering bottleneck. +**Action:** Implement a simple global memoization cache (`Map`) for `iconData` resolution to make icon object lookups O(1) on subsequent renders, avoiding redundant iterations across icon sets. diff --git a/client/src/components/base/IconifyIcon.tsx b/client/src/components/base/IconifyIcon.tsx index b48f0dc..88c820d 100644 --- a/client/src/components/base/IconifyIcon.tsx +++ b/client/src/components/base/IconifyIcon.tsx @@ -33,18 +33,35 @@ const iconSets: Record = { "mdi-light": mdiLightIcons, }; +const iconCache = new Map(); + +// ⚡ Bolt: Cache icon lookups to prevent O(n) iteration across icon sets on every render const iconData = (icon: string) => { + if (iconCache.has(icon)) { + return iconCache.get(icon); + } + const [prefix, name] = icon.includes(":") ? icon.split(":") : ["", icon]; + let result; if (prefix && iconSets[prefix]) { - const data = getIconData(iconSets[prefix], name); - if (data) return data; + result = getIconData(iconSets[prefix], name); + } + + if (!result) { + for (const [_, icons] of Object.entries(iconSets)) { + const data = getIconData(icons, name); + if (data) { + result = data; + break; + } + } } - for (const [_, icons] of Object.entries(iconSets)) { - const data = getIconData(icons, name); - if (data) return data; + if (result) { + iconCache.set(icon, result); } + return result; }; const IconifyIcon = ({