Разместите sentinel после списка и наблюдайте его через IntersectionObserver. Когда он появляется в области видимости, добавляйте следующую порцию.
Бесконечный скроллинг списка публикаций
Разместите sentinel после списка и наблюдайте его через IntersectionObserver. Когда он появляется в области видимости, добавляйте следующую порцию.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
В приложении, которое выводит список публикаций, требуется реализовать механизм бесконечной прокрутки: при достижении нижней части списка автоматически запрашиваются новые записи и добавляются к уже отображаемым.
const createPosts = () => {
return Array(10)
.fill(null)
.map((_, idx) => {
return { title: `Post-${idx}`, id: Math.random() * 10000 };
});
};
function Feed() {
const [items, setItems] = useState<Post[]>(createPosts());
return (
<div className="app">
{items.map((item) => (
<div className="post" key={item.id}>
{item.title}
</div>
))}
</div>
);
}
Ответ
Разместите sentinel после списка и наблюдайте его через IntersectionObserver. Когда он появляется в области видимости, добавляйте следующую порцию.
Для синхронного createPosts из условия:
function Feed() {
const [items, setItems] = React.useState(createPosts);
const end = React.useRef(null);
React.useEffect(() => {
let handled = false;
const observer = new IntersectionObserver(entries => {
if (!handled && entries.some(entry => entry.isIntersecting)) {
handled = true;
const next = createPosts();
setItems(current => [...current, ...next]);
}
}, { rootMargin: '200px' });
observer.observe(end.current);
return () => observer.disconnect();
}, [items.length]);
return <div>
{items.map(item => <div key={item.id}>{item.title}</div>)}
<div ref={end} aria-hidden='true' />
</div>;
}
Для настоящего API добавьте cursor, loading, hasMore, отмену и состояние ошибки. Случайные ID демонстрации не гарантируют уникальность: серверные записи должны иметь устойчивые ключи.