Updating dimensions of asynchronously loaded Next Image in ResizeObserver callback
Answered
Californian posted this in #help-forum
CalifornianOP
Hello,
In one of my components, I have a Next Image loaded with static dimensions, let's say 500 x 500 (let's call it
- Feature needed: whenever I observe changes in the dimensions of
- Current idea: I'm using
- Problem: Whenever there is a change in
- Previous try: bind
Any idea on how to solve this ?
Thanks in advance
In one of my components, I have a Next Image loaded with static dimensions, let's say 500 x 500 (let's call it
staticImage). I also have an array of images with dimensions loaded asynchronously through an useEffect hook (let's call it dynamicImages).dynamicImages are in an absolute div for the purpose of one of my features, so when the screen resizes, they don't receive dimension updates like staticImage (which is in a relative div).- Feature needed: whenever I observe changes in the dimensions of
staticImage (due to screen resizes, for example), I want to scale the dimensions of dynamicImages accordingly.- Current idea: I'm using
ResizeObserver API to observe dimension changes in staticImage and, whenever there is one, I scale dynamicImages accordingly.- Problem: Whenever there is a change in
staticImage dimensions, thus triggering the observer callback, dynamicImages aren't still loaded (remember, they are loaded asynchronously) and so I can't scale them at that moment.- Previous try: bind
dynamicImages to the useEffect hook that triggers the ResizeObserver, like the solution for my previous question (https://nextjs-forum.com/post/1190463330360500366). Unlike in the other case, this doesn't work here, because now updates to dynamicImages are done in the same useEffect hook, causing an infinite loop.Any idea on how to solve this ?
Thanks in advance
Answered by Californian
Solved by starting the observer right after
const dynamicImagesData = await imagesLoader(...); instead of in a separate useEffect hook, and iterating directly through dynamicImagesData instead dynamicImages (which comes asynchronously in a flow only controlled by React). Duh, what a stupid me 😕2 Replies
CalifornianOP
Here is an illustrative code for the situation, with all the relevant parts:
const staticImageRef = useRef<HTMLImageElement>(null);
const [dynamicImages, setDynamicImages] = useState<ImageInterface[]>([]);
const updateUploadedImageDimensions = (index: number, width: number, height: number) => {
setDynamicImages((prevImages) => {
const newImages = [...prevImages];
newImages[index].width = width;
newImages[index].height = height;
return newImages;
});
};
useEffect(() => {
const dynamicImagesData = await imagesLoader(...);
setDynamicImages(dynamicImagesData);
}, []);
useEffect(() => {
const resizeObserver = new ResizeObserver((entries) => {
if (entries && entries.length > 0){
const staticImageEntry = entries[0];
const { width, height } = staticImageEntry.contentRect;
for (let index = 0; index < dynamicImages.length; index++) {
updateUploadedImageDimensions(
index,
defaultDynamicImageDimensions.width * (width / defaultStaticImageDimensions.width),
defaultDynamicImageDimensions.height * (height / defaultStaticImageDimensions.height)
);
}
}
});
if (staticImageRef.current) {
resizeObserver.observe(staticImageRef.current);
}
return () => resizeObserver.disconnect();
}, []);
return (
<div className="relative">
<Image src=... width={defaultStaticImageDimensions.width} height={defaultStaticImageDimensions.height} priority ref={staticImageRef}/>
{dynamicImages.map((image, index) => (
<div className="absolute" key={image.id}>
<Image src={image.url} width={image.width} height={image.height}/>
</div>
))}
</div>
)CalifornianOP
Solved by starting the observer right after
const dynamicImagesData = await imagesLoader(...); instead of in a separate useEffect hook, and iterating directly through dynamicImagesData instead dynamicImages (which comes asynchronously in a flow only controlled by React). Duh, what a stupid me 😕Answer