Intersection Observer を使用して、実際にはページの下部にあるビューポートに div 要素があるかどうかを確認しています。useEffect フックでは、Firestore からブログをフェッチして状態に保存しています。下にスクロールした後、別のブログをフェッチするフェッチ関数が実行されます。以前のブログと新しいブログでブログの状態を設定していますが、なぜか以前のブログは空の配列です。新しいブログのみを出力し、古いブログはなくなりました。
getBlogs は、Firestore からドキュメントを取得し、取得したブログで新しい配列を作成しています。
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import BlogListItem from './BlogListItem';
import db from '../firebase/firebase';
import BlogFilters from './BlogFilters';
import loader from '../images/loader.gif';
import RecentFeed from './RecentFeed';
import BlogsContext from '../context/blogs-context';
let startDoc = null;
const getBlogs = docs => {
const blogs = [];
docs.forEach(doc => {
blogs.push({
id: doc.id,
...doc.data(),
});
});
startDoc = docs[docs.length - 1];
return blogs;
};
const firstFetchBlogs = async () => {
const data = await db.collection('blogs').orderBy('createdAt', 'desc').limit(5).get();
return getBlogs(data.docs);
};
const fetchBlogs = async () => {
const data = await db.collection('blogs').orderBy('createdAt', 'desc').startAfter(startDoc).limit(5).get();
return getBlogs(data.docs);
};
export const ReadBlogList = () => {
const [ blogs, setBlogs ] = useState([]);
const [ loading, setLoading ] = useState(true);
const [ smallLoading, setSmallLoading ] = useState(false);
const [ noMore, setNoMore ] = useState(false);
const scrollElement = React.useRef();
const paginateBlogs = () => {
setSmallLoading(true);
fetchBlogs()
.then(newBlogs => {
setBlogs([ ...blogs, ...newBlogs ]);
setSmallLoading(false);
})
.catch(() => {
setSmallLoading(false);
setNoMore(true);
});
};
const io = new IntersectionObserver(entries => {
const ratio = entries[0].intersectionRatio;
if (ratio > 0) {
paginateBlogs();
}
});
useEffect(() => {
let didCancel = false;
firstFetchBlogs().then(newBlogs => {
if (!didCancel) {
setBlogs(newBlogs);
setLoading(false);
io.observe(scrollElement.current);
}
});
return () => {
didCancel = true;
};
}, []);
return (
<div className="container read-blog-list">
<BlogFilters />
{loading && <img src={loader} alt="Loader" />}
{!loading && (
<BlogsContext.Provider value={{ blogs }}>
<div className="read-blog-list__content">
{blogs.length > 0 ? (
<div className="read-blog-list__blogs">
{blogs.map(blog => <BlogListItem to="read" key={blog.id} {...blog} />)}
<div ref={scrollElement} />
</div>
) : (
noMore && <p className="text-grey-darkest text-center text-xl">No more blogs</p>
)}
<RecentFeed />
{smallLoading && (
<div className="flex items-center justify-center">
<img className="w-12 h-12" src={loader} alt="Loader" />
</div>
)}
</div>
</BlogsContext.Provider>
)}
</div>
);
};
const mapStateToProps = state => ({
filters: state.filters,
});
export default connect(mapStateToProps)(ReadBlogList);