From e0e413800573b53147738886ad12f3c453ab025a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 11:00:13 +0000 Subject: [PATCH] feat(perf): memoize icon lookups in IconifyIcon Memoize the `iconData` results in `client/src/components/base/IconifyIcon.tsx` to turn $O(N)$ object property lookups into an $O(1)$ dictionary fetch, significantly reducing main thread block during massive layouts renders. Also fixed up caching null results to prevent repetitive missing-icon lookups. Added a learning to the Bolt journal. Co-authored-by: sshahriazz <34005640+sshahriazz@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ client/src/components/base/IconifyIcon.tsx | 25 +++++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..aef6e09 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,4 @@ + +## 2024-05-25 - Iconify Object Lookup Memoization +**Learning:** In the client application, the `IconifyIcon` wrapper component dynamically searches through massive JSON objects (`@iconify-json/*`) inside `getIconData()` for every rendered icon if caching is not implemented. Because the app uses icons heavily in layouts and UI components, omitting an upfront string-to-data cache causes repeated $O(N)$ lookup costs over thousands of keys that synchronously block the main thread. +**Action:** Always verify that high-frequency UI components, especially those decoding layout icons from massive dictionaries, memoize their lookup results using an $O(1)$ structure (like a `Map`) to prevent severe cascading render delays. diff --git a/client/src/components/base/IconifyIcon.tsx b/client/src/components/base/IconifyIcon.tsx index b48f0dc..059fa3e 100644 --- a/client/src/components/base/IconifyIcon.tsx +++ b/client/src/components/base/IconifyIcon.tsx @@ -33,18 +33,33 @@ const iconSets: Record = { "mdi-light": mdiLightIcons, }; +const iconCache = new Map(); + const iconData = (icon: string) => { + if (iconCache.has(icon)) { + return iconCache.get(icon); + } + const [prefix, name] = icon.includes(":") ? icon.split(":") : ["", icon]; + let data; if (prefix && iconSets[prefix]) { - const data = getIconData(iconSets[prefix], name); - if (data) return data; + data = getIconData(iconSets[prefix], name); } - for (const [_, icons] of Object.entries(iconSets)) { - const data = getIconData(icons, name); - if (data) return data; + if (!data) { + for (const [_, icons] of Object.entries(iconSets)) { + const foundData = getIconData(icons, name); + if (foundData) { + data = foundData; + break; + } + } } + + iconCache.set(icon, data); + + return data; }; const IconifyIcon = ({