英文:
My Custom useScrollPosition Hook isn't Working in Next JS, When I'm Trying to Fetch the Next Page of Results
问题
以下是翻译好的部分:
详情
我正在使用 tRPC 并使用 useInfiniteQuery 来创建 Next.js 版 Twitter 克隆中的无限滚动效果。我相信它正在正常工作。我已经进行了测试,可以获取下一页的推文。
然而,我已经制作了一个自定义钩子,用于检查用户当前在页面上的位置,但状态似乎根本没有更新。它只会在页面加载时获取滚动位置,然后停止更新。
Feed 组件的代码
// 依赖项
import Link from 'next/link';
import { useState, useEffect } from 'react';
// API
import { api } from '~/utils/api';
// 组件
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 限制
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 () => {
// 如果滚动位置在 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>{`嗯... 获取这些推特出现问题,请尝试刷新?`}</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"
>
主页
</Link>
</div>
);
}
return (
<div className="h-full">
{tweets.map((fullTweet) => (
<TweetView
tweetData={{ ...fullTweet }}
key={fullTweet.tweet.id}
input={{ limit: LIMIT }}
/>
))}
</div>
);
};
我已经做的
我已经尝试找到其他解决方案,比如使用 useCallback,并查看堆栈溢出上的其他类似问题,但没有任何解决方案有效。
我已经在这个问题上卡住一段时间了,如果这个问题不解决,我就无法继续项目。
英文:
Details
I'm using tRPC and using useInfiniteQuery to create the infinite scroll effect in a nextjs twitter clone. I believe that is working fine. I've tested it and I can fetch the next page of tweets.
However I've made a custom hook that will check where the users current position is on the page and the state doesn't appear to be updating at all. It will get the scroll position on page load and that's it.
Code for Feed Component
// 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>
);
};
What I've Done
I've tried finding other solutions, like useCallback, and looking at other similar questions on stack overflow but none of the solutions are working.
Been stumped on this for a while and I can't move forward on the project without this working.
答案1
得分: 0
当前临时解决方案
我今天需要确实让这个工作。我使用了一个名为react-intersection-observer的包。创建了一个div,它将在动态流的底部渲染。一旦这个带有交集引用的div进入视野,下一页将被获取。
我有点恼火,无论出于什么原因,我都无法让hooks正常工作。但这样做完全可以胜任。
如果有人遇到困难,以下是代码。
动态流组件
// 依赖项
import Link from 'next/link';
import { useEffect } from 'react';
import { useInView } from 'react-intersection-observer';
// API
import { api } from '~/utils/api';
// 组件
import { LoadingSpinner } from '~/components/LoadingSpinner';
import { TweetView } from '~/components/TweetView';
// 动态流限制
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 () => {
// 如果进入视野,且有下一页且未在获取数据时,获取下一页数据
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>{`哎呀...获取这些推文出了些问题,尝试刷新一下?`}</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"
>
主页
</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>
)}
</>
);
};
英文:
Current Hacky Solution
I needed to get this working today realistically. I used a package called react-intersection-observer. created a div that will render at the bottom of the feed. Once that div with the intersection ref comes into view the next page will be fetched.
I'm a bit annoyed that I can't get the hooks to work for whatever reason. But this does the job just fine.
Here's the code if anyone else is struggling.
Feed Component
// 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>
)}
</>
);
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论