Created
February 18, 2025 14:03
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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', | |
}} | |
/> | |
); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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