使用@egjs/react-infiniteGrid的GatsbyJS画廊重渲染时样式丢失问题排查
问题分析与解决方案
看起来你遇到的问题是状态更新触发父组件重渲染时,@egjs/react-infiniteGrid 的 Masonry 布局没有自动重新计算,导致网格定位样式丢失,图片变成单列排列。当你点击"Test"按钮切换showLightbox状态时,父组件Lighting会重新渲染,子组件GalleryComp也会跟着重渲染,但MasonryInfiniteGrid没有触发布局更新逻辑,原本的网格定位样式因此失效。
核心原因
- 父组件状态变化触发全局重渲染,
GalleryComp作为子组件被强制重新渲染,但MasonryInfiniteGrid没有感知到这次重渲染,未执行布局计算。 - 组件内的
items、images等数据每次渲染都重新生成,没有缓存优化,加重了不必要的重渲染,也干扰了网格组件的状态感知。
具体解决方案
1. 优化组件渲染,减少不必要的重渲染
用React.memo和useMemo/useCallback来缓存组件和计算结果,避免无意义的重渲染:
// Gallery.tsx import { memo, useMemo, useCallback } from "react"; // 用memo包裹Item组件,避免每次渲染都重新创建 const Item = memo(({ num, images }: any) => ( <div className="item h-auto" style={{ width: "250px" }} > <div className="thumbnail"> <GatsbyImage image={getImage(images[num])} alt="gallery image" className="rounded-xl shadow-xl" imgStyle={{ objectFit: "contain" }} /> </div> </div> )); // 用memo包裹GalleryComp,仅在props变化时重渲染 export default memo(function GalleryComp(props: any) { // 用useMemo缓存images,避免每次渲染都重新映射数据 const images = useMemo(() => { return props.query.allFile.edges.map( ({ node }) => node.childImageSharp.gatsbyImageData ); }, [props.query]); const numPics = useMemo(() => props.query.allFile.edges.length, [props.query]); // 用useCallback缓存getItems函数,避免每次渲染重新创建 const getItems = useCallback((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 [items, setItems] = React.useState(() => getItems(0, numPics)); const igRef = useRef(); return ( <> <MasonryInfiniteGrid className="container" gap={5} ref={igRef} onRequestAppend={(e) => { const nextGroupKey = (+e.groupKey! || 0) + 1; // 用函数式更新items,确保获取最新状态 setItems(prevItems => [...prevItems, ...getItems(nextGroupKey, numPics)]); }} > {items.map((item) => ( <Item data-grid-groupkey={item.groupKey} key={item.key} num={item.key} images={images} /> ))} </MasonryInfiniteGrid> </> ); });
2. 强制触发网格布局更新
在GalleryComp中添加useEffect,监听可能影响布局的变量,主动调用网格实例的layout方法重新计算布局:
// 加入GalleryComp组件内部 useEffect(() => { // 当组件重渲染或items变化时,强制触发布局计算 if (igRef.current) { igRef.current.layout(); } }, [items, numPics]);
3. 调整父组件的条件渲染逻辑
如果不需要Hello!元素和按钮同处一个容器,可以把条件渲染的元素移到画廊容器之外,减少对画廊组件渲染的干扰:
// Page.tsx 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> </div> {showLightbox && <p className="text-white text-center mb-10">Hello!</p>} <GalleryComp query={query} /> </div> </> );
验证效果
应用以上修改后,当你点击"Test"按钮切换状态时,GalleryComp不会被不必要地重渲染;即使触发重渲染,也会主动调用网格组件的布局方法,确保网格样式保持正常。
内容的提问来源于stack exchange,提问作者Jack Pollock
相关产品推荐
相关产品推荐

