T
TanStack•10mo ago
fascinating-indigo

Re-render issue: Masonry Grid implementation in Next.js

Hello, I've been facing this issue for a while now, I have a masonry grid that renders images from an external source, and when the image source is broken, I set the source to a default image that shows the image is broken. Now every time I scroll, the image starts loading again (because I have a shimmer effect flash for a sec), and it doesn't have to be a big scroll, it could be as little as little as a few pixels (~10px). On Firefox - All the images reload On Chrome (or Chromium based browsers) - Just the broken images load again Here is the code to the customized next/image component I'm using below;
export const CustomNextImage = React.memo(
({ src, alt, onError, onLoaded, hasError, ...rest }: NextImageProps) => {
const { theme } = useTheme();
const [URL, setURL] = useState<string>(src);

useEffect(() => {
setURL(src);
}, [hasError, src]);

const handleLoad = useCallback(() => {
onLoaded && onLoaded();
}, [onLoaded]);

const handleError = useCallback(() => {
setURL("/broken.png");
}, []);

const MemoizedURL = useMemo(() => URL, [URL]);

if (!MemoizedURL || MemoizedURL === "self" || !isValidImageURL(MemoizedURL)) return null;

return (
<div className="w-full h-full bg-background">
<Image
draggable={false}
{...rest}
src={MemoizedURL}
alt={alt}
fetchPriority="high"
decoding="async"
sizes="100vw"
width={100}
height={100}
loading="lazy"
placeholder={`data:image/svg+xml;base64,${toBase64(
shimmer(theme as "light" | "dark")
)}`}
quality={100}
onError={handleError}
/>
</div>
);
}
);
export const CustomNextImage = React.memo(
({ src, alt, onError, onLoaded, hasError, ...rest }: NextImageProps) => {
const { theme } = useTheme();
const [URL, setURL] = useState<string>(src);

useEffect(() => {
setURL(src);
}, [hasError, src]);

const handleLoad = useCallback(() => {
onLoaded && onLoaded();
}, [onLoaded]);

const handleError = useCallback(() => {
setURL("/broken.png");
}, []);

const MemoizedURL = useMemo(() => URL, [URL]);

if (!MemoizedURL || MemoizedURL === "self" || !isValidImageURL(MemoizedURL)) return null;

return (
<div className="w-full h-full bg-background">
<Image
draggable={false}
{...rest}
src={MemoizedURL}
alt={alt}
fetchPriority="high"
decoding="async"
sizes="100vw"
width={100}
height={100}
loading="lazy"
placeholder={`data:image/svg+xml;base64,${toBase64(
shimmer(theme as "light" | "dark")
)}`}
quality={100}
onError={handleError}
/>
</div>
);
}
);
Can anyone help me understand what might be going on? Thanks
2 Replies
national-gold
national-gold•10mo ago
hmm hard to say, but if the image are loaded again then it would mean that CustomNextImage is unmounted and mounted, i would suggest to start with that.
fascinating-indigo
fascinating-indigoOP•10mo ago
Yeah! Same thought that I had, I'm trying to see what could be causing that. I'll let you know what I find out Turns out it was an issue with the key prop on the parent component 😅

Did you find this page helpful?