Skip to content

Instantly share code, notes, and snippets.

@markosmk
Created February 18, 2025 14:03
Show Gist options
  • Save markosmk/361ed4a6a2eb536c37b4f2b1cee2d56b to your computer and use it in GitHub Desktop.
Save markosmk/361ed4a6a2eb536c37b4f2b1cee2d56b to your computer and use it in GitHub Desktop.
una alternativa a @tanstack/react-virtual u otras librerias similares (si lo que quieres hacer es basico), es usar la API IntersectionObserver de JavaScript para implementar lazy loading o renderizado virtual
import * as React from 'react';
import { useIntersectionObserver} from './use-intersection-observer';
export function FadeInOnScroll() {
const elementRef = React.useRef<HTMLDivElement | null>(null);
const entry = useIntersectionObserver(elementRef, { threshold: 0.5 });
return (
<div
ref={elementRef}
style={{
opacity: entry?.isIntersecting ? 1 : 0,
transition: 'opacity 0.5s ease-in-out',
height: '200px',
background: 'lightblue',
margin: '20px 0',
}}
>
Fade In on Scroll
</div>
);
};
import * as React from 'react';
import { useIntersectionObserver} from './use-intersection-observer';
export function InfiniteDataLoader() {
const [data, setData] = React.useState<number[]>(Array.from({ length: 10 }, (_, i) => i + 1));
const loaderRef = React.useRef<HTMLDivElement | null>(null);
const handleIntersect = React.useCallback((entry: IntersectionObserverEntry) => {
if (entry.isIntersecting) {
console.log('load more data...');
setTimeout(() => {
setData((prevData) => [...prevData, ...Array.from({ length: 10 }, (_, i) => prevData.length + i + 1)]);
}, 1000);
}
}, []);
useIntersectionObserver(loaderRef, {
threshold: 1,
onIntersect: handleIntersect,
});
return (
<div>
{data.map((item) => (
<div key={item} style={{ padding: '20px', border: '1px solid #ccc' }}>
Item {item}
</div>
))}
<div ref={loaderRef} style={{ height: '20px', background: '#f0f0f0' }}>
Loading...
</div>
</div>
);
};
import * as React from 'react';
import { useIntersectionObserver} from './use-intersection-observer';
export function InfiniteScroll() {
const [items, setItems] = React.useState<number[]>(Array.from({ length: 20 }, (_, i) => i + 1));
const loaderRef = React.useRef<HTMLDivElement | null>(null);
const entry = useIntersectionObserver(loaderRef, { threshold: 1 });
React.useEffect(() => {
if (entry?.isIntersecting) {
setTimeout(() => {
setItems((prevItems) => [...prevItems, ...Array.from({ length: 10 }, (_, i) => prevItems.length + i + 1)]);
}, 1000);
}
}, [entry]);
return (
<div>
{items.map((item) => (
<div key={item} style={{ padding: '20px', border: '1px solid #ccc' }}>
Item {item}
</div>
))}
<div ref={loaderRef} style={{ height: '20px', background: '#f0f0f0' }}>
Loading...
</div>
</div>
);
};
import * as React from 'react';
import { useIntersectionObserver} from './use-intersection-observer';
export function LazyImage({ src, alt }: { src: string; alt: string }) {
const imgRef = React.useRef<HTMLImageElement | null>(null);
const entry = useIntersectionObserver(imgRef, { threshold: 0.1 });
return (
<img
ref={imgRef}
src={entry?.isIntersecting ? src : undefined}
alt={alt}
style={{
width: '100%',
height: 'auto',
backgroundColor: '#f0f0f0',
}}
/>
);
};
import * as React from "react"
interface Args extends IntersectionObserverInit {
freezeOnceVisible?: boolean
debug?: boolean
onIntersect?: (entry: IntersectionObserverEntry) => void
}
export function useIntersectionObserver(
elementRef: React.RefObject<HTMLElement | null>,
{ threshold = 0, root = null, rootMargin = "0%", freezeOnceVisible = false, debug = false, onIntersect }: Args
): IntersectionObserverEntry | undefined {
const [entry, setEntry] = React.useState<IntersectionObserverEntry>()
React.useEffect(() => {
const node = elementRef?.current
const hasIOSupport = !!window.IntersectionObserver
if (!hasIOSupport || !node) return
const observerParams = { threshold, root, rootMargin }
const observer = new IntersectionObserver((entries) => {
const [entry] = entries
setEntry(entry)
if (debug) {
console.log("IntersectionObserver Entry:", entry)
}
if (onIntersect) {
onIntersect(entry)
}
if (freezeOnceVisible && entry.isIntersecting) {
observer.disconnect()
}
}, observerParams)
observer.observe(node)
return () => observer.disconnect()
}, [threshold, root, rootMargin, freezeOnceVisible, debug, onIntersect, elementRef])
return entry
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment