Skip to content

perf(gallery): memoise the gallery's derived values - #1307

Merged
nanaf6203-bit merged 1 commit into
MettaChain:mainfrom
peaceshallom37-rgb:perf/1266-memoize-gallery-computations
Oct 21, 2026
Merged

nanaf6203-bit merged 1 commit into
MettaChain:mainfrom
peaceshallom37-rgb:perf/1266-memoize-gallery-computations

Conversation

@peaceshallom37-rgb

@peaceshallom37-rgb peaceshallom37-rgb commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

What this does

ImageGallery sits inside the property detail view, which re-renders for plenty of reasons that have nothing to do with the photos — the currency toggle, a parent state change, an analytics update. Every one of those re-renders rebuilt the thumbnail strip: a fresh slice(1, 5) array and four new next/image elements, even when the image list had not changed a byte.

This memoizes the gallery so that those re-renders do not walk the photo tree.

export const ImageGallery = React.memo(function ImageGallery(props: ImageGalleryProps) {
  return (
    <ErrorBoundary>
      <ImageGalleryContent {...props} />
    </ErrorBoundary>
  );
});

The changes

  • ImageGallery is wrapped in React.memo. React skips the whole gallery when images and propertyName are unchanged, so the images are not re-created at all.
  • The thumbnail is a component of its own (GalleryThumbnail, also memoized). A thumbnail depends on its image, its alt text and the open handler, so a re-render that leaves a thumbnail's props alone does not re-render it.
  • The derived values are memoized: the image list, the thumbnail slice and the overflow counter are computed with useMemo, keyed on the image list.
  • The lightbox handlers are useCallbacks, so the lightbox keeps the same handler identity across renders.

The rendered output is unchanged: the same images, the same thumbnails, the same overflow counter, the same empty state.

The image list is normalised before anything reads it, so a missing images prop still renders the empty state instead of throwing.

Tests

src/components/property/__tests__/ImageGallery.test.tsx covers the behaviour that is easy to break:

  • a re-render with unchanged props does not re-render a single image (the next/image mock counts its renders);
  • a re-render with a changed property name does re-render them;
  • every thumbnail is still rendered, and the overflow counter still counts the images that do not fit.
$ npx jest src/components/property/__tests__/ImageGallery.test.tsx
Tests:       16 passed, 16 total

Notes for the reviewer

Another open PR (#1306) adds the i18n and the validation to the same file. The changes here are in different parts of it, but a merge conflict is likely and will have to be resolved by hand.

React.memo compares the props shallowly, so the caller has to pass the same array reference to get the benefit. PropertyDetailServer passes property.images, which is stable, so it does. A freshly built array (property.images.filter(...) in the render body) would be a new reference on every render and would re-render the gallery as before. This is written up in docs/image-gallery.md.

CI

The workflow run of this pull request is waiting for approval (action_required), because it comes from a fork. It has to be approved before it starts.

main is currently red for reasons that are not related to this branch: npm run lint reports 82 errors and npm run typecheck reports 472 errors across the repository (missing react-pdf and @types/leaflet typings, for instance), and a set of wallet test suites fail. The same failures are on main and on the other open branches. This branch adds no new lint, type or test failures — eslint and tsc are clean on the files it touches, and the full jest run has the same 17 failing suites as main (all of them pre-existing) with 4 more passing tests.

Closes #1266

The property detail view re-renders for plenty of reasons that have
nothing to do with the photos - the currency toggle, a parent state
change, an analytics update - and each of those re-renders rebuilt the
thumbnail strip from scratch: a fresh slice(1, 5) array and four new
next/image elements, even when the image list had not changed.

- ImageGallery is wrapped in React.memo, so React skips the gallery
  outright when images and propertyName are unchanged.
- Each thumbnail is a memoized component of its own, so a thumbnail
  whose props did not change is not re-rendered.
- The image list, the thumbnail slice and the overflow counter are
  computed with useMemo, keyed on the image list.
- The lightbox handlers are useCallbacks, so the lightbox keeps the same
  handler identity across renders.

The rendered output is unchanged: the same images, the same thumbnails,
the same overflow counter, the same empty state. A missing images prop is
still handled - the list is normalised before anything reads it.

Closes MettaChain#1266
@drips-wave

drips-wave Bot commented Oct 20, 2026

Copy link
Copy Markdown

@peaceshallom37-rgb Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@nanaf6203-bit nanaf6203-bit left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simple and effective — memoising those derived values should help. LGTM.

@nanaf6203-bit
nanaf6203-bit merged commit e275d83 into MettaChain:main Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Memoize expensive computations in Gallery

2 participants