为什么函数组件嵌套函数使用过时的状态值



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的长度,以避免过时。也许也可以通过裁判来解决这个问题,但我认为他们在这里没有必要。

最新更新