naver / egjs-infinitegrid

A module used to arrange card elements including content infinitely on a grid layout.
https://naver.github.io/egjs-infinitegrid/
MIT License
2.23k stars 95 forks source link

React - Image position styling disappearing on unrelated component show/hide (rerender) #507

Closed pollock83 closed 2 years ago

pollock83 commented 2 years ago

Description

I'm using react-infinitegrid to create a Masonry grid of image components to form a gallery on my GatsbyJS website. I would like to be able to show a lightbox containing a full screen image when one of the images in the gallery is clicked.

I am running into an issue where when I show/hide a component using selective rendering, all of the <div> components within the gallery lose their styling when a component is shown/hidden. This means that the grid breaks and I am left with just a long column of images. I have tested this also with just showing/hiding a <p> component on the page and experience the same behaviour.

Please see the video of the behaviour below. Notice that when I click my 'Test' button, and the 'Hello!' component is shown, all of the <div> components containing the images lose their position styling (you can see this update in the chrome developer view on the right).

Video: https://imgur.com/a/3PpYr6Z

Before: enter image description here

After: enter image description here

Page.tsx:

import React, { useState } from "react";
import { graphql, useStaticQuery } from "gatsby";
import GalleryComp from "../../components/gallery";
import Lightbox from "../../components/Lightbox";

export default function Lighting() {
    const [showLightbox, setShowLightbox] = useState(false);
    const [selectedImage, setSelectedImage] = useState(null);

    const query = useStaticQuery(graphql`
        query galleryQuery {
            allFile(filter: { absolutePath: { regex: "/images/test/" } }) {
                edges {
                    node {
                        childImageSharp {
                            gatsbyImageData(
                                placeholder: BLURRED
                                transformOptions: { fit: OUTSIDE }
                                width: 250
                            )
                            resize {
                                aspectRatio
                            }
                            fluid {
                                src
                            }
                        }
                        publicURL
                    }
                }
            }
        }
    `);

    return (
    <>
        <div className="mt-64">
            <div className="flex flex-col items-center justify-center mb-10">
                <button onClick={() => setShowLightbox(true)} className="text-white border-2 border-white rounded-xl p-2">Test</button>
                {showLightbox ? (
                    <p className="text-white">Hello!</p>
                ) : null}
            </div>

            <GalleryComp query={query} />
    </>
    );
}

Gallery.tsx:

import { GatsbyImage, getImage } from "gatsby-plugin-image";
import React, { useRef } from "react";
import { MasonryInfiniteGrid } from "@egjs/react-infinitegrid";
import { useEffect } from "react";

export default function GalleryComp(props: any) {
    const data = props.query;
    const images = data.allFile.edges.map(
        ({ node }) => node.childImageSharp.gatsbyImageData
    );

    const igRef = useRef();

    function getItems(nextGroupKey: number, count: number) {
        const nextItems = [];
        const nextKey = nextGroupKey * count;

        for (let i = 0; i < count; ++i) {
            nextItems.push({ groupKey: nextGroupKey, key: nextKey + i });
        }
        return nextItems;
    }

    const numPics = data.allFile.edges.length;
    console.log(numPics);
    const [items, setItems] = React.useState(() => getItems(0, numPics));

    const Item = ({ num }: any) => (
        <div
            className="item h-auto"
            style={{
                width: "250px",
            }}
        >
            <div className="thumbnail">
                <GatsbyImage
                    image={getImage(images[num])}
                    alt={"blah"}
                    className="rounded-xl shadow-xl"
                    imgStyle={{ objectFit: "contain" }}
                />
            </div>
        </div>
    );

    return (
        <>
            <MasonryInfiniteGrid
                className="container"
                gap={5}
                ref={igRef}
                onRequestAppend={(e) => {
                    const nextGroupKey = (+e.groupKey! || 0) + 1;
                    setItems([...items, ...getItems(nextGroupKey, numPics)]);
                }}
            >
                {items.map((item) => (
                    <Item
                        data-grid-groupkey={item.groupKey}
                        key={item.key}
                        num={item.key}
                    />
                ))}
            </MasonryInfiniteGrid>
        </>
    );
}

Thanks!

daybrush commented 2 years ago

@pollock83

https://codesandbox.io/s/elastic-browser-bhs8lu?file=/src/App.tsx

I made a demo similar to your code, but it works.


    const numPics = data.allFile.edges.length;
    console.log(numPics);

I think it's like a change in numPics. Did the value change before the button was pressed and after the button was pressed?

A change in this value causes duplication of key values. Check it.

pollock83 commented 2 years ago

@daybrush Thanks for your reply! :)

Unfortunately it doesn't seem to be a change in numPics. When I remove numPics and change the code to just get 10 items as a hard-coded value, the error still occurs...

Could it be something to do with GatsbyJS?

My current solution to workaround this issue is to wrap my Gallery component in a React.memo() to prevent it rerendering. This seems to work, but obviously isn't an ideal solution...

daybrush commented 2 years ago

@pollock83 There is a function called Item in GalleryComp.

Since Item is changed for every rendering, the Item is newly rendered.

Either move it out or wrap the Item with useMemo.

pollock83 commented 2 years ago

@daybrush That seems to have fixed it! Thanks for the help!