← back to Wine Finder Next
components/LazyWineCard.tsx
70 lines
'use client';
import dynamic from 'next/dynamic';
import { useState, useEffect, useRef } from 'react';
// Lazy load the actual WineCard component
const WineCard = dynamic(() => import('./WineCard'), {
loading: () => <WineCardSkeleton />,
ssr: false,
});
// Skeleton loader
function WineCardSkeleton() {
return (
<div className="bg-white rounded-xl p-4 shadow-md animate-pulse">
<div className="w-full h-48 bg-gray-300 rounded-lg mb-4"></div>
<div className="h-6 bg-gray-300 rounded mb-2"></div>
<div className="h-4 bg-gray-300 rounded mb-2 w-3/4"></div>
<div className="h-4 bg-gray-300 rounded mb-4 w-1/2"></div>
<div className="h-10 bg-gray-300 rounded"></div>
</div>
);
}
interface LazyWineCardProps {
wine: any;
onPriceCheck?: (wine: any) => void;
}
export default function LazyWineCard({ wine, onPriceCheck }: LazyWineCardProps) {
const [isVisible, setIsVisible] = useState(false);
const cardRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setIsVisible(true);
observer.unobserve(entry.target);
}
});
},
{
rootMargin: '100px',
threshold: 0.01,
}
);
if (cardRef.current) {
observer.observe(cardRef.current);
}
return () => {
if (cardRef.current) {
observer.unobserve(cardRef.current);
}
};
}, []);
return (
<div ref={cardRef}>
{isVisible ? (
<WineCard wine={wine} onPriceCheck={onPriceCheck} />
) : (
<WineCardSkeleton />
)}
</div>
);
}