My组件使用无限滚动(通过IntersectionObserver
(获取并显示帖子。组件进行的API调用取决于当前获取的帖子数。这将作为偏移量传递给API调用。
所需的效果是,如果posts
数组为空,则其长度为0,并且我的API将返回前5个post。当UI中的最后一个帖子与视口相交时,会进行另一次获取,但这一次API调用传递的偏移量为5,因此API将跳过集合中的前5个帖子,转而返回下一个5。
这是我的代码:
export default function Feed() {
const [posts, setPosts] = useState([]);
const fetchPosts = async () => {
const newPosts = await postAPI.getFeed(posts.length);
setPosts((prevPosts) => [...prevPosts, ...newPosts]);
};
useEffect(fetchPosts, []);
const observerRef = useRef(
new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
observerRef.current.unobserve(entry.target);
fetchPosts();
}
})
);
const observePost = useCallback((node) => node && observerRef.current.observe(node), []);
return (
<>
{posts.map((post) => {
const key = post._id;
const isLastPost = key === posts.at(-1)._id;
const callbackRef = !isLastPost ? undefined : observePost; //only if last post in state, watch it!
return <PostCard {...{ key, post, callbackRef }} />;
})}
</>
);
}
但是,每次调用fetchPosts
函数时,它用于posts.length
的值都是0——该函数最初创建时使用的数字。
有人能向我解释一下为什么posts.length
的闭包在这里已经过时了吗?我想,每次组件重新渲染时,其中所有嵌套的函数都是从头开始重新创建的吗?因此,fetchPosts
函数每次被调用时肯定都应该使用posts.length
的最新值吗?感谢您的帮助!:(
您在每个渲染上都创建了一个new IntersectionObserver
,这不是一个好主意。然而,只有所有这些创建的观察者中的第一个,即存储在ref中的观察者(从未更新的ref(,才是调用observe()
的观察者,并且第一个观察者对posts
的初始值使用过时的闭包。
相反,我建议在observePost
函数中创建交叉口观测器:
const [posts, setPosts] = useState([]);
const fetchPosts = useCallback(async () => {
const newPosts = await postAPI.getFeed(posts.length);
setPosts((prevPosts) => [...prevPosts, ...newPosts]);
}, [posts.length]);
// ^^^^^^^^^^^^
useEffect(fetchPosts, []);
const observePost = useCallback((node) => {
if (!node) return;
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
observer.unobserve(entry.target);
fetchPosts();
}
});
observer.observe(node);
}, [fetchPosts]);
// ^^^^^^^^^^
这里重要的是observePost
回调对fetchPosts
回调的依赖性,后者依赖于posts
的长度,以避免过时。也许也可以通过裁判来解决这个问题,但我认为他们在这里没有必要。