무한 스크롤 (intersection observer)
import React, { useCallback, useEffect, useRef, useState } from 'react';
const mockFetch = (page: number) => {
return new Promise<string[]>((resolve) => {
setTimeout(() => {
resolve(
Array.from({ length: 10 }, (_, i) => `Item ${(page - 1) * 10 + i + 1}`)
);
}, 1000);
});
};
const InfiniteScroll: React.FC = () => {
const [items, setItems] = useState<string[]>([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const observerRef = useRef<IntersectionObserver | null>(null);
const loaderRef = useRef<HTMLDivElement | null>(null);
const loadMoreItems = useCallback(async () => {
if (isLoading) return;
setIsLoading(true);
const newItems = await mockFetch(page);
setItems((prevItems) => [...prevItems, ...newItems]);
setIsLoading(false);
if (newItems.length === 0) {
setHasMore(false);
} else {
setPage((prevPage) => prevPage + 1);
}
}, [isLoading, page]);
useEffect(() => {
if (!loaderRef.current || !hasMore) return;
observerRef.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) {
loadMoreItems();
}
});
observerRef.current.observe(loaderRef.current);
return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
};
}, [loadMoreItems, hasMore]);
return (
<div style={{ height: '100px' }}>
<h1>Infinite Scroll Example</h1>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
{isLoading && <p>Loading...</p>}
{!hasMore && <p>No more items to load</p>}
<div
ref={loaderRef}
style={{ height: '20px', backgroundColor: 'transparent' }}
/>
</div>
);
};
export default InfiniteScroll;
debounce (useRef)
import { useRef } from 'react';
const useDebounce = (callback: (...args: any) => void, delay: number) => {
const timeoutRef = useRef<number | null>(null);
return (...args: unknown[]) => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = window.setTimeout(() => {
callback(...args);
}, delay);
};
}
export default useDebounce;
throttle (useRef)
import { useRef } from 'react';
const useThrottle = (callback: (...args: any) => void, delay: number) => {
const throttleRef = useRef(false);
return (...args: unknown[]) => {
if (throttleRef.current) return;
throttleRef.current = true;
setTimeout(() => {
callback(...args);
throttleRef.current = false;
}, delay);
};;
}
export default useThrottle;