I’m fairly new to React and currently need to implement infinite scrolling. It’s working, however, every time new content is loaded the whole Feed component re-renders and I’ve been scratching my head over it for the whole day.
I’m using an Intersection Observer. Every time the scroll reaches the bottom of the page I use a custom hook to make a call loading 6 more elements.
This is the custom hook:
const usePosts = (pageNum = 1) => {
const [results, setResults] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [error, setError] = useState({});
const [hasNextPage, setHasNextPage] = useState(false);
const { category } = useArticlesContext();
useEffect(() => {
setIsLoading(true);
setIsError(false);
setError({});
const controller = new AbortController();
const { signal } = controller;
getPostsPageArticles(category, pageNum, { signal })
.then((data) => {
console.log(data);
setResults((prev) => [...prev, ...data]);
setHasNextPage(Boolean(data.length));
setIsLoading(false);
})
.catch((e) => {
setIsLoading(false);
if (signal.aborted) return;
setIsError(true);
setError({ message: e.message });
});
return () => controller.abort();
}, [pageNum, category]);
useEffect(() => {
setResults([]);
}, [category]);
return { isLoading, isError, error, results, hasNextPage };
};
And this is the component in question:
const Feed = () => {
const [pageNum, setPageNum] = useState(1);
const { isLoading, isError, error, results, hasNextPage } = usePosts(pageNum);
const intObserver = useRef();
const lastPostRef = useCallback(
(post) => {
if (isLoading) return;
if (intObserver.current) intObserver.current.disconnect();
intObserver.current = new IntersectionObserver((posts) => {
if (posts[0].isIntersecting && hasNextPage) {
console.log('We are near the last post');
setPageNum((prev) => prev + 1);
}
});
if (post) intObserver.current.observe(post);
},
[isLoading, hasNextPage],
);
if (isLoading) return <div>Loading...</div>;
if (isError) return <div>Error: {error.message}</div>;
const content = results.map((post, i) => {
if (results.length === i + 1) {
return <ArticleCard ref={lastPostRef} article={post} />;
}
return <ArticleCard article={post} />;
});
return (
<div className="feed-container">
{content}
{isLoading && <p>Loading more posts...</p>}
</div>
);
};
The ArticleCard component in the content variable is this:
const ArticleCard = React.forwardRef(({ article }, ref) => {
const articleCardBody = (
<>
<div className="article-card__title">
<h2>{article.title}</h2>
</div>
<img src={article.img} alt="" className="article-card__img" />
<div className="article-card__content">
<p>{article.content}</p>
</div>
</>
);
const content = ref ? (
<div className="article-card" ref={ref}>
{articleCardBody}
</div>
) : (
<div className="article-card">{articleCardBody}</div>
);
return content;
});
Any help would be much appreciated, I’m losing my mind.
Thank you.