I have a component that looks like this:
import React, { FC, ImgHTMLAttributes, useEffect, useRef, useState } from "react";
import NextImage from "next/image";
import checkInViewIntersectionObserver from "@/utils/isInViewPortIntersectionObserver";
import PlaceIcon from "./PlaceIcon";
export interface NcImageProps extends ImgHTMLAttributes<HTMLImageElement> {
containerClassName?: string;
}
const NcImage: FC<NcImageProps> = ({
containerClassName = "",
alt = "nc-imgs",
src = "",
className = "object-cover w-full h-full",
...args
}) => {
const isMounted = useRef(false);
const _containerRef = useRef(null);
const [__src, set__src] = useState("");
const [imageLoaded, setImageLoaded] = useState(false);
useEffect(() => {
let _imageEl: HTMLImageElement | null = null;
isMounted.current = true;
const _imageOnViewPort = () => {
if (!src) {
_handleImageLoaded();
return true;
}
_imageEl = new Image();
if (_imageEl) {
_imageEl.src = src;
_imageEl.addEventListener("load", _handleImageLoaded);
}
return true;
};
const _handleImageLoaded = () => {
if (!isMounted.current) return;
setImageLoaded(true);
set__src(src);
};
const _checkInViewPort = () => {
if (!_containerRef.current) return;
checkInViewIntersectionObserver({
target: _containerRef.current,
options: {
root: null,
rootMargin: "0%",
threshold: 0,
},
freezeOnceVisible: true,
callback: _imageOnViewPort,
});
};
const _initActions = async () => {
_checkInViewPort();
};
_initActions();
return () => {
isMounted.current = false;
};
}, [src]);
const renderLoadingPlaceholder = () => {
return (
<div
className={`${className} flex items-center justify-center bg-neutral-200 dark:bg-neutral-6000 text-neutral-100 dark:text-neutral-500`}
>
<div className="h-2/4 max-w-[50%]">
<PlaceIcon />
</div>
</div>
);
};
return (
<div className={`nc-NcImage ${containerClassName}`} data-nc-id="NcImage" ref={_containerRef}>
{__src && imageLoaded ? (
<NextImage src={__src} className={className} alt={alt} {...args} />
) : (
renderLoadingPlaceholder()
)}
</div>
);
};
export default NcImage;
It was originally using <img ...
instead of NextImage
and things were fine. I wanted to change it to Next.js' Image
component. As soon as I did that, I got the following error:
Type '{ crossOrigin?: "" | "anonymous" | "use-credentials" | undefined; decoding?: "async" | "auto" | "sync" | undefined; height?: string | number | undefined; loading?: "eager" | "lazy" | undefined; ... 263 more ...; alt: string; }' is not assignable to type '{ src: string | StaticImport; alt: string; width?: SafeNumber | undefined; height?: SafeNumber | undefined; fill?: boolean | undefined; loader?: ImageLoader | undefined; ... 11 more ...; lazyRoot?: string | undefined; }'.
Types of property 'width' are incompatible.
Type 'string | number | undefined' is not assignable to type 'SafeNumber | undefined'.
Type 'string' is not assignable to type 'SafeNumber | undefined'.ts(2322)
I'm not entirely sure how to fix it though since I am not using width
anywhere.
I thought of just getting it from the inputs to NcImage
and then changing its type:
const newWidth = typeof width === 'number' ? SafeNumber(width) : width;
But I'm not sure what SafeNumber is or where to import it from. What is the proper way to fix this problem?
CodePudding user response:
Since you are rendering a Next.js Image
tag, use the corresponding props type, which is ImageProps
, like so:
import { ImageProps } from "next/image";
export interface NcImageProps extends ImageProps {
containerClassName?: string;
}
With this, your error goes away.