diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..1c3beb4 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,4 @@ +## YYYY-MM-DD - [Memoize grid item re-rendering] + +**Learning:** `useMemo` can significantly boost the performance of long list or grid components (like `PhotoGrid` component) that otherwise gets re-rendered on simple interactions like navigating the image lightbox. Running standard formatting `pnpm format` applies huge changes across entire directories, so it's generally best to format and clean up ONLY modified files to avoid huge diff pollution in code review. +**Action:** Be extremely cautious of running format/lint commands at the root codebase; specify only modified directories or files, or avoid formatting if it cascades globally. Focus on targeted memoization where state changes dictate expensive array mapping operations. diff --git a/app/[...path]/PhotoGrid.tsx b/app/[...path]/PhotoGrid.tsx index 757a0be..c3638ef 100644 --- a/app/[...path]/PhotoGrid.tsx +++ b/app/[...path]/PhotoGrid.tsx @@ -8,7 +8,7 @@ 'use client'; -import { useState, useCallback, useEffect } from 'react'; +import { useState, useCallback, useEffect, useMemo } from 'react'; import Image from 'next/image'; import { Lightbox } from '@/components/Lightbox'; import { FadeIn } from '@/components/FadeIn'; @@ -136,50 +136,56 @@ export function PhotoGrid({ assets, layout = 'masonry', gridStyle }: PhotoGridPr }; }, [lightboxIndex, closeLightbox, goNext, goPrev]); + // Memoize grid items to prevent re-renders of the entire grid (which includes + // Image and FadeIn components) when navigating the lightbox. + const gridItems = useMemo(() => { + return assets.map((asset, index) => ( + +
openLightbox(index)} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + openLightbox(index); + } + }} + role="button" + tabIndex={0} + aria-label={`View photo ${index + 1}`} + style={{ + ...(asset.dominantColor ? { backgroundColor: asset.dominantColor } : {}), + ...((layout === 'masonry' || layout === 'showcase') && asset.aspectRatio + ? { aspectRatio: `${asset.aspectRatio}` } + : {}), + }} + > + + {(asset.camera || asset.lens) && ( +
+ {[asset.camera, asset.lens, asset.focalLength].filter(Boolean).join(' · ')} +
+ )} +
+
+ )); + }, [assets, layout, openLightbox]); + return ( <>
- {assets.map((asset, index) => ( - -
openLightbox(index)} - onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - openLightbox(index); - } - }} - role="button" - tabIndex={0} - aria-label={`View photo ${index + 1}`} - style={{ - ...(asset.dominantColor ? { backgroundColor: asset.dominantColor } : {}), - ...((layout === 'masonry' || layout === 'showcase') && asset.aspectRatio - ? { aspectRatio: `${asset.aspectRatio}` } - : {}), - }} - > - - {(asset.camera || asset.lens) && ( -
- {[asset.camera, asset.lens, asset.focalLength].filter(Boolean).join(' · ')} -
- )} -
-
- ))} + {gridItems}
{lightboxIndex !== null && (