详细信息
我正在使用tRPC和useInfiniteQuery在nextjs twitter克隆中创建无限滚动效果。我相信这很有效。我已经测试过了,我可以获取下一页的推文。
然而,我制作了一个自定义挂钩,它将检查用户当前位置在页面上的位置,并且状态似乎根本没有更新。它会得到页面加载时的滚动位置,就这样
饲料成分代码
// Dependencies
import Link from 'next/link';
import { useState, useEffect } from 'react';
// API
import { api } from '~/utils/api';
// Components
import { LoadingSpinner } from '~/components/LoadingSpinner';
import { TweetView } from '~/components/TweetView';
function useScrollPosition() {
const [scrollPosition, setScrollPosition] = useState(0);
const handleScroll = () => {
const height =
document.documentElement.scrollHeight -
document.documentElement.clientHeight;
const winScroll =
document.body.scrollTop || document.documentElement.scrollTop;
const scrolled = (winScroll / height) * 100;
setScrollPosition(scrolled);
};
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
return scrollPosition;
}
// Feed Limit
const LIMIT = 10;
export const Feed = () => {
const {
data,
isLoading: tweetsLoading,
hasNextPage,
fetchNextPage,
isFetching,
} = api.tweet.getAll.useInfiniteQuery(
{
limit: LIMIT,
},
{
getNextPageParam: (lastPage) => lastPage.nextCursor,
}
);
const scrollPosition = useScrollPosition();
const tweets = data?.pages.flatMap((page) => page.tweetsWithUsers) ?? [];
console.log(scrollPosition);
useEffect(() => {
const nextTweetPage = async () => {
// Get the next page of data if the scroll position is at x
if (scrollPosition > 90 && hasNextPage && !isFetching) {
await fetchNextPage();
}
};
nextTweetPage().catch((e) => console.log(e));
}, [scrollPosition, hasNextPage, isFetching, fetchNextPage]);
if (tweetsLoading) {
return (
<div className="mt-4 flex h-screen items-center justify-center">
<LoadingSpinner size={40} />
</div>
);
}
if (!data) {
return (
<div className="flex flex-col items-center justify-center gap-6 p-6 text-center">
<h3>{`Hmmm... Something went wrong getting these twoots, try refreshing?`}</h3>
<Link
href="/"
className="rounded-full bg-bright-pink py-2.5 px-3.5 text-base font-bold text-white shadow-sm hover:bg-pink-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-bright-pink"
>
Home
</Link>
</div>
);
}
return (
<div className="h-full">
{tweets.map((fullTweet) => (
<TweetView
tweetData={{ ...fullTweet }}
key={fullTweet.tweet.id}
input={{ limit: LIMIT }}
/>
))}
</div>
);
};
我做了什么
我试着找到其他解决方案,比如useCallback,并研究了其他关于堆栈溢出的类似问题,但这些解决方案都不起作用。
我在这个问题上被难住了一段时间,如果没有这个工作,我就无法推进这个项目。
当前的破解解决方案
我今天需要现实地完成这项工作。我使用了一个名为react intersection observer的软件包。创建了一个div,该div将在提要的底部进行渲染。一旦具有交集ref的div进入视图,就会获取下一页。
无论出于什么原因,我都无法让挂钩工作,这让我有点恼火。但这做得很好。
如果其他人正在挣扎,下面是代码。
进纸组件
// Dependencies
import Link from 'next/link';
import { useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
// API
import { api } from '~/utils/api';
// Components
import { LoadingSpinner } from '~/components/LoadingSpinner';
import { TweetView } from '~/components/TweetView';
// Feed Limit
const LIMIT = 10;
export const Feed = () => {
const {
data,
isLoading: tweetsLoading,
hasNextPage,
fetchNextPage,
isFetching,
} = api.tweet.getAll.useInfiniteQuery(
{
limit: LIMIT,
},
{
getNextPageParam: (lastPage) => lastPage.nextCursor,
}
);
const { ref, inView } = useInView({ threshold: 0 });
const tweets = data?.pages.flatMap((page) => page.tweetsWithUsers) ?? [];
useEffect(() => {
const nextTweetPage = async () => {
// Get the next page of data if the intersection comes into view
if (inView && hasNextPage && !isFetching) {
await fetchNextPage();
}
};
nextTweetPage().catch((e) => console.log(e));
}, [inView, hasNextPage, isFetching, fetchNextPage]);
if (tweetsLoading) {
return (
<div className="mt-4 flex h-screen items-center justify-center">
<LoadingSpinner size={40} />
</div>
);
}
if (!data) {
return (
<div className="flex flex-col items-center justify-center gap-6 p-6 text-center">
<h3>{`Hmmm... Something went wrong getting these twoots, try refreshing?`}</h3>
<Link
href="/"
className="rounded-full bg-bright-pink py-2.5 px-3.5 text-base font-bold text-white shadow-sm hover:bg-pink-700 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-bright-pink"
>
Home
</Link>
</div>
);
}
return (
<>
<div className="h-full">
{tweets.map((fullTweet) => (
<TweetView
tweetData={{ ...fullTweet }}
key={fullTweet.tweet.id}
input={{ limit: LIMIT }}
/>
))}
</div>
<div ref={ref}></div>
{isFetching && (
<div className="flex h-auto w-full items-center justify-center p-4">
<LoadingSpinner size={40} />
</div>
)}
</>
);
};