khi sử dụng các trang web, đặc biệt là mạng xã hội, có lẽ các bạn đã không ít lần nhìn thấy họ sử dụng kỹ thuật Infinite Scroll (cuộn vô hạn). Đây là kỹ thuật dùng đễ lấy một lượng dữ liệu cần thiết để hiển thị lên trang web và chỉ lấy thêm dữ liệu khi người dùng cuộn đến cuối trang. Trong bài viết này, mình và các bạn sẽ cùng tìm hiểu cách để tạo ra một Infinite Scroll Component sử dụng React Hooks.
Bắt đầu thôi:
Trong bài viết này mình sẽ sử dụng api từ JSONPlaceholder và mình chỉ tập trung vào xây dựng Infinite Scroll Component mà không quá chú trọng về UI.
Đầu tiên mình sẽ tạo vài state đơn giản phục vụ cho việc pagination và dùng useEffect để fetch dữ liệu ban đầu.
function App() {
const [page, setPage] = useState(1);
const [totalRows, setTotalRows] = useState(0);
const [posts, setPosts] = useState([]);
useEffect(() => {
(async () => {
const response = await (
await fetch(
`http://js-post-api.herokuapp.com/api/posts?_page=${page}&_limit=10`
)
).json();
setPosts([...posts, ...response.data]);
setTotalRows(response.pagination._totalRows);
})();
}, [page]);
}
return (
<div>
<InfiniteScroll
loader={<p>loading...</p>}
className="w-[800px] mx-auto my-10"
fetchMore={() => setPage((prev) => prev + 1)}
hasMore={posts.length < totalRows}
endMessage={<p>You have seen it all</p>}
>
{posts.map((post, index) => (
<div
className="rounded-xl shadow-md mb-8 flex items-center p-5"
key={index}
>
<img src={post.imageUrl} className="rounded-full w-14 h-14" />
<div className="ml-5">
<h3 className="font-medium">{post.author}</h3>
<h1 className="font-bold text-xl">{post.title}</h1>
<p>{post.description}</p>
</div>
</div>
))}
</InfiniteScroll>
</div>
);
}
Cách thức hoạt động của component InfiniteScroll là khi cuộn đến cuối sẽ gọi hàm fetchMore
để setPage
tăng lên 1, lúc này useEffect sẽ chạy lại và fetch posts page tiếp theo để set và posts.
import React, { useEffect, useRef } from "react";
const InfiniteScroll = ({
children,
loader,
fetchMore,
hasMore,
endMessage,
className,
}) => {
const pageEndRef = useRef(null);
useEffect(() => {
if (hasMore) {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting) { // kiểm tra element có nằm trong viewport không?
fetchMore();
}
});
if (pageEndRef.current) {
observer.observe(pageEndRef.current);
}
return () => {
if (pageEndRef.current) {
observer.unobserve(pageEndRef.current);
}
};
}
}, [hasMore]);
return (
<div className={className}>
{children}
{hasMore ? <div ref={pageEndRef}>{loader}</div> : endMessage}
</div>
);
};
export default InfiniteScroll;
Đây là mặt mũi ứng dụng của chúng ta.
Khi cuộn đến cuối trang.
Khi ở page cuối.
Tạm kết
Vậy là chúng ta đã xây dựng thành công Infinite Scroll component cho ứng dụng React cơ bản. Mặc dù giao diện còn sơ sài nhưng hy vọng sẽ giúp ích được cho các bạn. Cảm ơn các bạn đã đọc bài viết.