Refactoring a React Photo Gallery - 2022-11-13
Refactoring & creating modern functionality on a React photo gallery
As I was working to update and add some photos on a clients website, specifically a photo gallery of their projects, when I came across a TODO in my documentation regarding the photo gallery.
TODO: Refactor / modernize gallery; with onClick? carousel/scroll?
So I was at a crossroads, as of now, the photo gallery is exactly what they are looking for and they love how it looked. Do I take the time and make it better? OF COURSE, why not complicate my afternoon...
So I set out to make this better. I wanted a click feature to open the image and I wanted to be able to scroll through the pictures without having to close and open each photo.
Luckily, there is always a great NPM package to be used. In my case I found 2 that work well with each other
react-images
react-photo-gallery
Here is my gallery component
function PhotoGallery() {
const [currentImage, setCurrentImage] = useState(0);
const [viewerIsOpen, setViewerIsOpen] = useState(false);
const openLightbox = useCallback((event, { photo, index }) => {
setCurrentImage(index);
setViewerIsOpen(true);
}, []);
const closeLightbox = () => {
setCurrentImage(0);
setViewerIsOpen(false);
};
return (
<>
<Grid container direction="column">
<Gallery photos={photos} onClick={openLightbox} />
</Grid>
<Box>
<ModalGateway>
{viewerIsOpen ? (
<Modal onClose={closeLightbox}>
<Carousel
currentIndex={currentImage}
views={photos.map((x) => ({
...x,
srcset: x.srcSet,
caption: x.title,
}))}
/>
</Modal>
) : null}
</ModalGateway>
</Box>
</>
);
}
export default PhotoGallery;
Let me explain my thought process.
Component State
// The current selected index / image position in the list of images in the gallery
const [currentImage, setCurrentImage] = useState(0);
// Flag to track if the modal is open or closed
const [viewerIsOpen, setViewerIsOpen] = useState(false);
Methods
After spending sometime troubleshooting rendering / onclick bugs, I decided to go with the useCallback hook to prevent the function from being recreated unless necessary.
Quick Note: I am still working to master "advanced" hooks. So, I wanted explain the difference between useMemo & useCallBack because they are similar.
The main difference is that useMemo returns a memoized value and useCallback returns a memoized function.
My understanding of memoization is basically caching a value so that it does not need to be recalculated until you need it.
// The onClick function for each image, which will open the modal, and will darken the background.
const openLightbox = useCallback((event, { photo, index }) => {
setCurrentImage(index);
setViewerIsOpen(true);
}, []);
// Setting the modal flag, and clearing the image tracked image index.
const closeLightbox = () => {
setCurrentImage(0);
setViewerIsOpen(false);
};
Render Method
For this project I am using the Material UI component library v5.5.3. I didn't include the styles because I am lazy, so style with your imagination :)
return (
<>
<Grid container direction="column">
/* photos is a json file of all images
EXAMPLE JSON
photos: [{
src: "img/path/imageName.jpg", // or whatever extension you have
width: X, // customized width specific to the image
height: X, // customized height specific to the image
}],
*/
// Gallery component will display all images with the onClick method for each.
<Gallery photos={photos} onClick={openLightbox} />
</Grid>
<Box>
// ModalGateway will insert the modal just before the end of the <body /> tag.
<ModalGateway>
// Conditional rendering of the modal, based on the state of the current modal flag.
{viewerIsOpen ? (
<Modal onClose={closeLightbox}>
<Carousel
currentIndex={currentImage}
views={photos.map((x) => ({
...x,
srcset: x.srcSet,
caption: x.title,
}))}
/>
</Modal>
) : null}
</ModalGateway>
</Box>
</>
);
Thats it! The creators of these packages make it super simple to use but if you need more information (like what other props these components take) here is what I used. Good Luck!
Official react-image documentation
Official react-photo-gallery documentation
Another step by step example
React useCallBack documentation
